diff options
| author | Stefan Boberg <[email protected]> | 2025-11-07 14:49:13 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-11-07 14:49:13 +0100 |
| commit | 24e43a913f29ac3b314354e8ce5175f135bcc64f (patch) | |
| tree | ca442937ceeb63461012b33a4576e9835099f106 /thirdparty/ryml/ext/c4core | |
| parent | get oplog attachments (#622) (diff) | |
| download | zen-24e43a913f29ac3b314354e8ce5175f135bcc64f.tar.xz zen-24e43a913f29ac3b314354e8ce5175f135bcc64f.zip | |
switch to xmake for package management (#611)
This change removes our dependency on vcpkg for package management, in favour of bringing some code in-tree in the `thirdparty` folder as well as using the xmake build-in package management feature. For the latter, all the package definitions are maintained in the zen repo itself, in the `repo` folder.
It should now also be easier to build the project as it will no longer depend on having the right version of vcpkg installed, which has been a common problem for new people coming in to the codebase. Now you should only need xmake to build.
* Bumps xmake requirement on github runners to 2.9.9 to resolve an issue where xmake on Windows invokes cmake with `v144` toolchain which does not exist
* BLAKE3 is now in-tree at `thirdparty/blake3`
* cpr is now in-tree at `thirdparty/cpr`
* cxxopts is now in-tree at `thirdparty/cxxopts`
* fmt is now in-tree at `thirdparty/fmt`
* robin-map is now in-tree at `thirdparty/robin-map`
* ryml is now in-tree at `thirdparty/ryml`
* sol2 is now in-tree at `thirdparty/sol2`
* spdlog is now in-tree at `thirdparty/spdlog`
* utfcpp is now in-tree at `thirdparty/utfcpp`
* xmake package repo definitions is in `repo`
* implemented support for sanitizers. ASAN is supported on windows, TSAN, UBSAN, MSAN etc are supported on Linux/MacOS though I have not yet tested it extensively on MacOS
* the zencore encryption implementation also now supports using mbedTLS which is used on MacOS, though for now we still use openssl on Linux
* crashpad
* bumps libcurl to 8.11.0 (from 8.8.0) which should address a rare build upload bug
Diffstat (limited to 'thirdparty/ryml/ext/c4core')
255 files changed, 73515 insertions, 0 deletions
diff --git a/thirdparty/ryml/ext/c4core/.github/release.sh b/thirdparty/ryml/ext/c4core/.github/release.sh new file mode 100644 index 000000000..68d24d3d0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/release.sh @@ -0,0 +1,129 @@ +#!/bin/bash + + +# useful to iterate when fixing the release: +# ver=0.2.1 ; ( set -x ; git tag -d v$ver ; git push origin :v$ver ) ; (set -x ; set -e ; tbump --only-patch --non-interactive $ver ; git add -u ; git commit --amend --no-edit ; git tag --annotate --message "v$ver" "v$ver" ; git push -f --tags origin ) + + +function c4_release_create() +{ + ( \ + set -euxo pipefail ; \ + ver=$(_c4_validate_ver $1) ; \ + branch=$(_c4_validate_branch) ; \ + c4_release_bump $ver ; \ + c4_release_commit $ver $branch \ + ) +} + +function c4_release_redo() +{ + ( \ + set -euxo pipefail ; \ + ver=$(_c4_validate_ver $1) ; \ + branch=$(_c4_validate_branch) ; \ + c4_release_delete $ver ; \ + c4_release_bump $ver ; \ + c4_release_amend $ver $branch \ + ) +} + +function c4_release_bump() +{ + ( \ + set -euxo pipefail ; \ + ver=$(_c4_validate_ver $1) ; \ + tbump --non-interactive --only-patch $ver \ + ) +} + +function c4_release_commit() +{ + ( \ + set -euxo pipefail ; \ + ver=$(_c4_validate_ver $1) ; \ + branch=$(_c4_validate_branch) ; \ + tag=v$ver ; \ + git add -u ; \ + git commit -m $tag ; \ + git tag --annotate --message $tag $tag ; \ + ) +} + +function c4_release_amend() +{ + ( \ + set -euxo pipefail ; \ + ver=$(_c4_validate_ver $1) ; \ + branch=$(_c4_validate_branch) ; \ + tag=v$ver ; \ + git add -u ; \ + git commit --amend -m $tag ; \ + git tag --annotate --message $tag $tag ; \ + ) +} + +function c4_release_delete() +{ + ( \ + set -euxo pipefail ; \ + ver=$(_c4_validate_ver $1) ; \ + git tag -d v$ver ; \ + git push origin :v$ver \ + ) +} + +function c4_release_push() +{ + ( \ + set -euxo pipefail ; \ + ver=$(_c4_validate_ver $1) ; \ + branch=$(_c4_validate_branch) ; \ + tag=v$ver ; \ + git push origin $branch ; \ + git push --tags origin $tag \ + ) +} + +function c4_release_force_push() +{ + ( \ + set -euxo pipefail ; \ + ver=$(_c4_validate_ver $1) ; \ + branch=$(_c4_validate_branch) ; \ + tag=v$ver ; \ + git push -f origin $branch ; \ + git push -f --tags origin $tag \ + ) +} + +function _c4_validate_ver() +{ + ver=$1 + if [ -z "$ver" ] ; then \ + exit 1 + fi + ver=$(echo $ver | sed "s:v\(.*\):\1:") + #sver=$(echo $ver | sed "s:\([0-9]*\.[0-9]*\..[0-9]*\).*:\1:") + if [ ! -f changelog/$ver.md ] ; then \ + if [ -f changelog/current.md ] ; then + git mv changelog/current.md changelog/$ver.md + touch changelog/current.md + git add changelog/current.md + else + echo "ERROR: could not find changelog/$ver.md or changelog/current.md" + exit 1 + fi + fi + echo $ver +} + +function _c4_validate_branch() +{ + branch=$(git rev-parse --abbrev-ref HEAD) + if [ "$branch" != "master" ] ; then + echo "ERROR: release branch must be master" + exit 1 + fi + echo $branch +} diff --git a/thirdparty/ryml/ext/c4core/.github/reqs.sh b/thirdparty/ryml/ext/c4core/.github/reqs.sh new file mode 100644 index 000000000..9937616af --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/reqs.sh @@ -0,0 +1,314 @@ +#!/usr/bin/env bash + +set -x + +# input environment variables: +# OS: the operating system +# CXX_: the compiler version. eg, g++-9 or clang++-6.0 +# BT: the build type +# VG: whether to install valgrind +# ARM: whether to arm cross-compiler and emulator +# GITHUB_WORKFLOW: when run from github +# API: whether to install swig +# CMANY: whether to install cmany + + + +#------------------------------------------------------------------------------- + +function c4_install_test_requirements() +{ + os=$1 + case "$os" in + ubuntu*) + c4_install_test_requirements_ubuntu + return 0 + ;; + macos*) + c4_install_test_requirements_macos + return 0 + ;; + win*) + c4_install_test_requirements_windows + return 0 + ;; + *) + return 0 + ;; + esac +} + +function c4_install_test_requirements_windows() +{ + if [ "$CMANY" == "ON" ] ; then + pip install cmany + fi + if [ "$API" == "ON" ] ; then + choco install swig + which swig + fi + # ensure chocolatey does not override cmake's cpack + which cpack + choco_cpack="/c/ProgramData/Chocolatey/bin/cpack.exe" + if [ -f $choco_cpack ] ; then + newname=$(echo $choco_cpack | sed 's:cpack:choco-cpack:') + mv -vf $choco_cpack $newname + fi + which cpack +} + +function c4_install_test_requirements_macos() +{ + if [ "$CMANY" == "ON" ] ; then + sudo pip3 install cmany + fi +} + +function c4_install_test_requirements_ubuntu() +{ + UBUNTU_RELEASE=$(lsb_release -rs) + UBUNTU_RELEASE_NAME=$(lsb_release -cs) + APT_PKG="" # all + PIP_PKG="" + c4_gather_test_requirements_ubuntu + echo "apt packages: $APT_PKG" + echo "pip packages: $PIP_PKG" + c4_install_test_requirements_ubuntu_impl + echo 'INSTALL COMPLETE!' +} + + +function c4_install_all_possible_requirements_ubuntu() +{ + export CXX_=all + export BT=Coverage + APT_PKG="" # all + PIP_PKG="" + sudo dpkg --add-architecture i386 + c4_gather_test_requirements_ubuntu + _c4_add_arm_compilers + echo "apt packages: $APT_PKG" + echo "pip packages: $PIP_PKG" + c4_install_test_requirements_ubuntu_impl + echo 'INSTALL COMPLETE!' +} + + +function c4_gather_test_requirements_ubuntu() +{ + if [ "$GITHUB_WORKFLOW" != "" ] ; then + sudo dpkg --add-architecture i386 + else + _add_apt build-essential + _add_apt cmake + fi + + _add_apt linux-libc-dev:i386 + _add_apt libc6:i386 + _add_apt libc6-dev:i386 + _add_apt libc6-dbg:i386 + _c4_addlibcxx + + _c4_gather_compilers "$CXX_" + + _add_apt python3-setuptools + _add_apt python3-pip + + #_add_apt iwyu + #_add_apt cppcheck + #_add_pip cpplint + # oclint? + if [ "$VG" == "ON" ] ; then + _add_apt valgrind + fi + + if [ "$BT" == "Coverage" ]; then + _add_apt lcov + _add_apt libffi-dev + _add_apt libssl-dev + _add_pip requests[security] + _add_pip pyopenssl + _add_pip ndg-httpsclient + _add_pip pyasn1 + _add_pip cpp-coveralls + fi + + if [ "$CMANY" != "" ] ; then + _add_pip cmany + fi + + case "$CXX_" in + arm*) + _c4_add_arm_compilers + ;; + esac +} + + +function c4_install_test_requirements_ubuntu_impl() +{ + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add - + wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | sudo apt-key add - + sudo -E apt-add-repository --yes "deb https://apt.kitware.com/ubuntu/ $UBUNTU_RELEASE_NAME main" + sudo -E add-apt-repository --yes ppa:ubuntu-toolchain-r/test + + if [ "$APT_PKG" != "" ] ; then + #sudo -E apt-get clean + sudo -E apt-get update + sudo -E apt-get install -y --force-yes $APT_PKG + fi + + if [ "$PIP_PKG" != "" ]; then + sudo pip3 install $PIP_PKG + fi +} + + +#------------------------------------------------------------------------------- + +function _c4_add_arm_compilers() +{ + # this is going to be deprecated: + # https://askubuntu.com/questions/1243252/how-to-install-arm-none-eabi-gdb-on-ubuntu-20-04-lts-focal-fossa + sudo -E add-apt-repository --yes ppa:team-gcc-arm-embedded/ppa + + _add_apt gcc-arm-embedded + _add_apt g++-arm-linux-gnueabihf + _add_apt g++-multilib-arm-linux-gnueabihf + _add_apt qemu +} + + +function _c4_gather_compilers() +{ + cxx=$1 + case $cxx in + g++-12 ) _c4_addgcc 12 ;; + g++-11 ) _c4_addgcc 11 ;; + g++-10 ) _c4_addgcc 10 ;; + g++-9 ) _c4_addgcc 9 ;; + g++-8 ) _c4_addgcc 8 ;; + g++-7 ) _c4_addgcc 7 ;; + g++-6 ) _c4_addgcc 6 ;; + g++-5 ) _c4_addgcc 5 ;; + #g++-4.9 ) _c4_addgcc 4.9 ;; # https://askubuntu.com/questions/1036108/install-gcc-4-9-at-ubuntu-18-04 + g++-4.8 ) _c4_addgcc 4.8 ;; + clang++-13 ) _c4_addclang 13 ;; + clang++-12 ) _c4_addclang 12 ;; + clang++-11 ) _c4_addclang 11 ;; + clang++-10 ) _c4_addclang 10 ;; + clang++-9 ) _c4_addclang 9 ;; + clang++-8 ) _c4_addclang 8 ;; + clang++-7 ) _c4_addclang 7 ;; + clang++-6.0) _c4_addclang 6.0 ;; + clang++-5.0) _c4_addclang 5.0 ;; + clang++-4.0) _c4_addclang 4.0 ;; + clang++-3.9) _c4_addclang 3.9 ;; + all) + all="g++-11 g++-10 g++-9 g++-8 g++-7 g++-6 g++-5 clang++-12 clang++-11 clang++-10 clang++-9 clang++-8 clang++-7 clang++-6.0 clang++-5.0 clang++-4.0 clang++-3.9" + echo "installing all compilers: $all" + for cxx in $all ; do + _c4_gather_compilers $cxx + done + ;; + "") + # use default compiler + ;; + arm*) + ;; + *) + echo "unknown compiler: $cxx" + exit 1 + ;; + esac +} + +# add a gcc compiler +function _c4_addgcc() +{ + gccversion=$1 + case $gccversion in + 5 ) + _add_apt gcc-5 "deb http://dk.archive.ubuntu.com/ubuntu/ xenial main" + _add_apt gcc-5 "deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe" + ;; + *) + ;; + esac + _add_apt g++-$gccversion + _add_apt g++-$gccversion-multilib + _add_apt libstdc++-$gccversion-dev + _add_apt lib32stdc++-$gccversion-dev +} + +# add a clang compiler +function _c4_addclang() +{ + clversion=$1 + case $clversion in + # in 18.04, clang9 and later require PPAs + 9 | 10 | 11 | 12 | 13) + _add_apt clang-$clversion "deb http://apt.llvm.org/$UBUNTU_RELEASE_NAME/ llvm-toolchain-$UBUNTU_RELEASE_NAME-$clversion main" + # libstdc++ is required + _c4_addgcc 11 + _c4_addgcc 10 + _c4_addgcc 9 + ;; + "") + _add_apt clang + ;; + *) + _add_apt clang-$clversion + ;; + esac + _add_apt g++-multilib # this is required for 32 bit https://askubuntu.com/questions/1057341/unable-to-find-stl-headers-in-ubuntu-18-04 + _add_apt clang-tidy-$clversion +} + +# add libc++ +function _c4_addlibcxx() +{ + _add_apt clang + _add_apt libc++1 + _add_apt libc++abi-dev + _add_apt libc++-dev + #_add_apt libc++1:i386 + #_add_apt libc++abi-dev:i386 + #_add_apt libc++-dev:i386 +} + + +#------------------------------------------------------------------------------- + +# add a pip package to the list +function _add_pip() +{ + pkgs=$* + PIP_PKG="$PIP_PKG $pkgs" + echo "adding to pip packages: $pkgs" +} + +# add a debian package to the list +function _add_apt() +{ + pkgs=$1 + sourceslist=$2 + APT_PKG="$APT_PKG $pkgs" + echo "adding to apt packages: $pkgs" + _add_src "$sourceslist" "# for packages: $pkgs" +} + +# add an apt source +function _add_src() +{ + sourceslist=$1 + comment=$2 + if [ ! -z "$sourceslist" ] ; then + echo "adding apt source: $sourceslist" + sudo bash -c "cat >> /etc/apt/sources.list <<EOF +$comment +$sourceslist +EOF" + #cat /etc/apt/sources.list + fi +} diff --git a/thirdparty/ryml/ext/c4core/.github/setenv.sh b/thirdparty/ryml/ext/c4core/.github/setenv.sh new file mode 100644 index 000000000..a13bcadbf --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/setenv.sh @@ -0,0 +1,443 @@ +#!/usr/bin/env bash + +set -e +set -x + +PROJ_DIR=$(pwd) + +function c4_show_info() +{ + set +x + env | sort + echo "PROJ_DIR=$PROJ_DIR" + echo "PROJ_PFX_TARGET=$PROJ_PFX_TARGET" + echo "PROJ_PFX_CMAKE=$PROJ_PFX_CMAKE" + echo "CMAKE_FLAGS=$CMAKE_FLAGS" + echo "NUM_JOBS_BUILD=$NUM_JOBS_BUILD" + echo "GITHUB_WORKSPACE=$GITHUB_WORKSPACE" + pwd + ls -lFhp + echo "BITLINKS=$BITLINKS" + for bl in shared64 static64 shared32 static32 arm64 arm64shared arm64static shared64arm static64arm arm32 arm32shared arm32static shared32arm static32arm arm ; do + if _c4skipbitlink $bl ; then + echo "skip $bl" + else + echo "exec $bl" + fi + done + echo "CXX_=$CXX_" + echo "BT=$BT" + echo "LINT=$LINT" + echo "SAN=$SAN" + echo "SAN_ONLY=$SAN" + echo "VG=$VG" + echo "BM=$BM" + echo "STD=$STD" + echo "ARM=$ARM" + echo "LIBCXX=$LIBCXX" + echo "VERBOSE_MAKEFILES=$VERBOSE_MAKEFILES" + which cmake + cmake --version + case "$CXX_" in + xcode) + # https://gist.github.com/nlutsenko/ee245fbd239087d22137 + echo "number of cores=$(sysctl -n hw.ncpu)" + #defaults read com.apple.dt.xcodebuild | grep -i Number | grep -i Build + #defaults read com.apple.dt.Xcode | grep -i Number | grep -i Tasks + ;; + gcc*|g++*|*clang*) + echo "number of cores=$(nproc)" + $CXX_ --version + ;; + esac + set -x + git branch + git rev-parse HEAD + git tag || echo + git log -1 --format='%H' +} + +function _c4bits() +{ + case "$1" in + shared64|static64|static64arm|shared64arm|arm64static|arm64shared|arm64) echo 64 ;; + shared32|static32|static32arm|shared32arm|arm32static|arm32shared|arm32|arm) echo 32 ;; + *) exit 1 ;; + esac +} + +function _c4linktype() +{ + case "$1" in + shared64|shared32|arm64static|arm32static|static64arm|static32arm) echo shared ;; + static64|static32|arm64shared|arm32shared|shared64arm|shared32arm|arm64|arm32|arm) echo static ;; + *) exit 1 ;; + esac +} + +function _c4vsarchtype() +{ + # https://cmake.org/cmake/help/git-stage/generator/Visual%20Studio%2016%202019.html + case "$1" in + shared64|static64) echo x64 ;; + shared32|static32) echo Win32 ;; + arm64|arm64shared|arm64static|shared64arm|static64arm) echo ARM64 ;; + arm32|arm32shared|arm32static|shared32arm|static32arm|arm) echo ARM ;; + *) exit 1 ;; + esac +} + +function _c4skipbitlink() +{ + bitlink___=$1 + if [ -z "$BITLINKS" ] ; then + return 1 # return nonzero as failure, meaning DO NOT SKIP + fi + for bl___ in $BITLINKS ; do + if [ "${bl___}" == "${bitlink___}" ] ; then + return 1 # return nonzero as failure, meaning DO NOT SKIP + fi + done + return 0 # return nonzero as success, meaning DO SKIP +} + +function c4_build_test() +{ + c4_build_target $* test-build +} + +function c4_run_test() +{ + c4_run_target $* test +} + +function c4_build_target() # runs in parallel +{ + if _c4skipbitlink "$1" ; then return 0 ; fi + id=$1 + target=$2 + if [ ! -z "$target" ] ; then + target="--target $target" + fi + build_dir=`pwd`/build/$id + export CTEST_OUTPUT_ON_FAILURE=1 + # watchout: the `--parallel` flag to `cmake --build` is broken: + # https://discourse.cmake.org/t/parallel-does-not-really-enable-parallel-compiles-with-msbuild/964/10 + # https://gitlab.kitware.com/cmake/cmake/-/issues/20564 + cmake --build $build_dir --config $BT $target -- $(_c4_generator_build_flags) $(_c4_parallel_build_flags) +} + +function c4_run_target() # does not run in parallel +{ + if _c4skipbitlink "$1" ; then return 0 ; fi + id=$1 + target=$2 + build_dir=`pwd`/build/$id + export CTEST_OUTPUT_ON_FAILURE=1 + cmake --build $build_dir --config $BT --target $target -- $(_c4_generator_build_flags) +} + +function c4_package() +{ + if _c4skipbitlink "$1" ; then return 0 ; fi + id=$1 + generator=$2 + build_dir=`pwd`/build/$id + if [ -z "$generator" ] ; then + c4_run_target $id package + else + ( cd $build_dir ; cpack -G $generator ) + fi +} + +function c4_submit_coverage() +{ + if [ "$BT" != "Coverage" ] ; then + echo "build type is \"$BT\": no coverage to submit" + return 0 + fi + if _c4skipbitlink "$1" ; then return 0 ; fi + id=$1 + coverage_service=$2 + build_dir=`pwd`/build/$id + echo "Submitting coverage data: $build_dir --> $coverage_service" + cmake --build $build_dir --config $BT --target ${PROJ_PFX_TARGET}coverage-submit-$coverage_service +} + +# WIP +function c4_run_static_analysis() +{ + if _c4skipbitlink "$1" ; then return 0 ; fi + id=$1 + linktype=$(_c4linktype $id) + build_dir=`pwd`/build/$id + # https://blog.kitware.com/static-checks-with-cmake-cdash-iwyu-clang-tidy-lwyu-cpplint-and-cppcheck/ + pushd $PROJ_DIR +} + +function c4_cfg_test() +{ + if _c4skipbitlink "$1" ; then return 0 ; fi + id=$1 + # + build_dir=`pwd`/build/$id + install_dir=`pwd`/install/$id + mkdir -p $build_dir + mkdir -p $install_dir + # + if [ "$TOOLCHAIN" != "" ] ; then + toolchain_file=`pwd`/$TOOLCHAIN + if [ ! -f "$toolchain_file" ] ; then + echo "ERROR: toolchain not found: $toolchain_file" + exit 1 + fi + _addcmkflags -DCMAKE_TOOLCHAIN_FILE=$toolchain_file + else + bits=$(_c4bits $id) + linktype=$(_c4linktype $id) + case "$linktype" in + static) _addcmkflags -DBUILD_SHARED_LIBS=OFF ;; + shared) _addcmkflags -DBUILD_SHARED_LIBS=ON ;; + *) + echo "ERROR: unknown linktype: $linktype" + exit 1 + ;; + esac + fi + if [ "$STD" != "" ] ; then + _addcmkflags -DC4_CXX_STANDARD=$STD + _addprojflags CXX_STANDARD=$STD + fi + if [ "$LIBCXX" != "" ] ; then + _addprojflags USE_LIBCXX=$LIBCXX + fi + # + if [ "$DEV" != "OFF" ] ; then + _addprojflags DEV=ON + fi + case "$LINT" in + all ) _addprojflags LINT=ON LINT_TESTS=ON LINT_CLANG_TIDY=ON LINT_PVS_STUDIO=ON ;; + clang-tidy) _addprojflags LINT=ON LINT_TESTS=ON LINT_CLANG_TIDY=ON LINT_PVS_STUDIO=OFF ;; + pvs-studio) _addprojflags LINT=ON LINT_TESTS=ON LINT_CLANG_TIDY=OFF LINT_PVS_STUDIO=ON ;; + * ) _addprojflags LINT=OFF ;; + esac + case "$SAN" in + ALL) _addprojflags SANITIZE=ON ;; + A ) _addprojflags SANITIZE=ON ASAN=ON TSAN=OFF MSAN=OFF UBSAN=OFF ;; + T ) _addprojflags SANITIZE=ON ASAN=OFF TSAN=ON MSAN=OFF UBSAN=OFF ;; + M ) _addprojflags SANITIZE=ON ASAN=OFF TSAN=OFF MSAN=ON UBSAN=OFF ;; + UB ) _addprojflags SANITIZE=ON ASAN=OFF TSAN=OFF MSAN=OFF UBSAN=ON ;; + * ) _addprojflags SANITIZE=OFF ;; + esac + case "$SAN_ONLY" in + ON) _addprojflags SANITIZE_ONLY=ON ;; + * ) _addprojflags SANITIZE_ONLY=OFF ;; + esac + case "$VG" in + ON) _addprojflags VALGRIND=ON VALGRIND_SGCHECK=OFF ;; # FIXME SGCHECK should be ON + * ) _addprojflags VALGRIND=OFF VALGRIND_SGCHECK=OFF ;; + esac + case "$BM" in + ON) _addprojflags BUILD_BENCHMARKS=ON ;; + * ) _addprojflags BUILD_BENCHMARKS=OFF ;; + esac + if [ "$BT" == "Coverage" ] ; then + # the coverage repo tokens can be set in the travis environment: + # export CODECOV_TOKEN=....... + # export COVERALLS_REPO_TOKEN=....... + _addprojflags COVERAGE_CODECOV=ON COVERAGE_CODECOV_SILENT=OFF + _addprojflags COVERAGE_COVERALLS=ON COVERAGE_COVERALLS_SILENT=OFF + fi + if [ ! -z "$VERBOSE_MAKEFILES" ] ; then + _addcmkflags -DCMAKE_VERBOSE_MAKEFILES=$VERBOSE_MAKEFILES + fi + _addcmkflags -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + if [ ! -z "$CMAKE_FLAGS" ] ; then + _addcmkflags $CMAKE_FLAGS + fi + + echo "building with additional cmake flags: $CMFLAGS" + + export C4_EXTERN_DIR=`pwd`/build/extern + mkdir -p $C4_EXTERN_DIR + + cmake --version + pwd + + # + # bash quote handling is a fiasco, and I could not find a way of storing + # quoted strings in variables and then expand the variables with correct quotes + # so we have to do this precious jewell of chicanery: + case "$CXX_" in + vs2022) + cmake -S $PROJ_DIR -B $build_dir -DCMAKE_INSTALL_PREFIX="$install_dir" \ + -G 'Visual Studio 17 2022' -A $(_c4vsarchtype $id) \ + $(_c4_add_ehsc_to_vs_arm32 $id) \ + -DCMAKE_BUILD_TYPE=$BT $CMFLAGS + ;; + vs2019) + cmake -S $PROJ_DIR -B $build_dir -DCMAKE_INSTALL_PREFIX="$install_dir" \ + -G 'Visual Studio 16 2019' -A $(_c4vsarchtype $id) \ + $(_c4_add_ehsc_to_vs_arm32 $id) \ + -DCMAKE_BUILD_TYPE=$BT $CMFLAGS + ;; + vs2017) + case "$bits" in + 64) g="Visual Studio 15 2017 Win64" ;; + 32) g="Visual Studio 15 2017" ;; + *) exit 1 ;; + esac + cmake -S $PROJ_DIR -B $build_dir -DCMAKE_INSTALL_PREFIX="$install_dir" \ + $(_c4_add_ehsc_to_vs_arm32 $id) \ + -DCMAKE_BUILD_TYPE=$BT -G "$g" $CMFLAGS + ;; + xcode) + g=Xcode + case "$bits" in + 64) a="x86_64" ;; + 32) a="i386" + echo "xcode does not support i386" + exit 1 # i386 is deprecated in xcode + ;; + esac + cmake -S $PROJ_DIR -B $build_dir -DCMAKE_INSTALL_PREFIX="$install_dir" \ + -DCMAKE_BUILD_TYPE=$BT -G "$g" -DCMAKE_OSX_ARCHITECTURES=$a $CMFLAGS + ;; + arm*|"") # make sure arm* comes before *g++ or *gcc* + cmake -S $PROJ_DIR -B $build_dir -DCMAKE_INSTALL_PREFIX="$install_dir" \ + -DCMAKE_BUILD_TYPE=$BT $CMFLAGS + ;; + *g++*|*gcc*|*clang*) + export CC_=$(echo "$CXX_" | sed 's:clang++:clang:g' | sed 's:g++:gcc:g') + _c4_choose_clang_tidy $CXX_ + cmake -S $PROJ_DIR -B $build_dir -DCMAKE_INSTALL_PREFIX="$install_dir" \ + -DCMAKE_BUILD_TYPE=$BT $CMFLAGS \ + -DCMAKE_C_COMPILER=$CC_ -DCMAKE_CXX_COMPILER=$CXX_ \ + -DCMAKE_C_FLAGS="-std=c99 -m$bits" -DCMAKE_CXX_FLAGS="-m$bits" + cmake --build $build_dir --target help | sed 1d | sort + ;; + em++) + emcmake cmake -S $PROJ_DIR -B $build_dir -DCMAKE_INSTALL_PREFIX="$install_dir" \ + -DCMAKE_BUILD_TYPE=$BT $CMFLAGS -DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" + ;; + *) + echo "unknown compiler" + exit 1 + ;; + esac +} + +function _c4_choose_clang_tidy() +{ + cxx=$1 + # only for clang compilers. + case $cxx in + clang*) + # try with version first + clang_tidy_ver=$(echo $cxx | sed "s:++:-tidy:") + clang_tidy=$(echo $cxx | sed "s:++.*:-tidy:") + for n in $clang_tidy_ver $clang_tidy ; do + exe=$(which $n) + echo "searching for $n: $exe" + if [ -z "$exe" ] ; then + echo "could not find $clang_tidy" + else + _addcmkflags "-DCLANG_TIDY=$exe" + return 0 + fi + done + echo "error: could not find clang-tidy for $cxx" + exit 1 + ;; + esac +} + +# add cmake flags without project prefix +function _addcmkflags() +{ + for f in $* ; do + CMFLAGS="$CMFLAGS ${f}" + done +} + +# add cmake flags with project prefix +function _addprojflags() +{ + for f in $* ; do + CMFLAGS="$CMFLAGS -D${PROJ_PFX_CMAKE}${f}" + done +} + +function _c4_add_ehsc_to_vs_arm32() +{ + id=$1 + case "$CXX_" in + vs*) + case "$id" in + arm32|arm32shared|arm32static|shared32arm|static32arm|arm) + echo '-DCMAKE_CXX_FLAGS="/EHsc"' + ;; + *) + esac + ;; + esac +} + +function _c4_parallel_build_flags() +{ + case "$CXX_" in + vs2022|vs2019|vs2017|vs2015) + # https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-command-line-reference?view=vs-2019 + # https://stackoverflow.com/questions/2619198/how-to-get-number-of-cores-in-win32 + if [ -z "$NUM_JOBS_BUILD" ] ; then + echo "/maxcpucount:$NUMBER_OF_PROCESSORS" + else + echo "/maxcpucount:$NUM_JOBS_BUILD" + fi + ;; + xcode) + # https://stackoverflow.com/questions/5417835/how-to-modify-the-number-of-parallel-compilation-with-xcode + # https://gist.github.com/nlutsenko/ee245fbd239087d22137 + if [ -z "$NUM_JOBS_BUILD" ] ; then + echo "-IDEBuildOperationMaxNumberOfConcurrentCompileTasks=$(sysctl -n hw.ncpu)" + else + echo "-IDEBuildOperationMaxNumberOfConcurrentCompileTasks=$NUM_JOBS_BUILD" + fi + ;; + *g++*|*gcc*|*clang*|em++) + if [ -z "$NUM_JOBS_BUILD" ] ; then + echo "-j $(nproc)" + else + echo "-j $NUM_JOBS_BUILD" + fi + ;; + "") # allow empty compiler + ;; + *) + echo "unknown compiler" + exit 1 + ;; + esac +} + +function _c4_generator_build_flags() +{ + case "$CXX_" in + vs2022|vs2019|vs2017|vs2015) + ;; + xcode) + # WTF??? + # https://github.com/biojppm/rapidyaml/pull/97/checks?check_run_id=1504677928#step:7:964 + # https://stackoverflow.com/questions/51153525/xcode-10-unable-to-attach-db-error + echo "-UseModernBuildSystem=NO" + ;; + *g++*|*gcc*|*clang*|em++) + ;; + "") # allow empty compiler + ;; + *) + echo "unknown compiler" + exit 1 + ;; + esac +} diff --git a/thirdparty/ryml/ext/c4core/.github/vagrant/Vagrantfile b/thirdparty/ryml/ext/c4core/.github/vagrant/Vagrantfile new file mode 100644 index 000000000..6f3d9d237 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/vagrant/Vagrantfile @@ -0,0 +1,80 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# 1) download and install vagrant: https://www.vagrantup.com/downloads.html +# (do not install ubuntu's 14.04 16.04 version, see https://stackoverflow.com/questions/22717428/vagrant-error-failed-to-mount-folders-in-linux-guest ): +# 2) vagrant plugin install vagrant-vbguest +# 3) vagrant up --provider virtualbox +# 4) vagrant ssh + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure(2) do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://atlas.hashicorp.com/search. + config.vm.box = "generic/ubuntu2004" + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + #config.ssh.username = 'travis' + #config.ssh.password = 'travis' + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + config.vm.synced_folder "../../../..", "/vagrant" + + #config.vm.synced_folder '.', '/vagrant', disabled: true + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + # end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies + # such as FTP and Heroku are also available. See the documentation at + # https://docs.vagrantup.com/v2/push/atlas.html for more information. + # config.push.define "atlas" do |push| + # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" + # end + + # Enable provisioning with a shell script. Additional provisioners such as + # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the + # documentation for more information about their specific syntax and use. + #config.vm.provision "shell", path: "travis-install.sh" + +end diff --git a/thirdparty/ryml/ext/c4core/.github/vagrant/macos/Vagrantfile b/thirdparty/ryml/ext/c4core/.github/vagrant/macos/Vagrantfile new file mode 100644 index 000000000..62806c970 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/vagrant/macos/Vagrantfile @@ -0,0 +1,71 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure("2") do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + # Every Vagrant development environment requires a box. You can search for + # boxes at https://vagrantcloud.com/search. + config.vm.box = "ramsey/macos-catalina" + config.vm.box_version = "1.0.0" + + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # NOTE: This will enable public access to the opened port + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine and only allow access + # via 127.0.0.1 to disable public access + # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" + + # Create a private network, which allows host-only access to the machine + # using a specific IP. + # config.vm.network "private_network", ip: "192.168.33.10" + + # Create a public network, which generally matched to bridged network. + # Bridged networks make the machine appear as another physical device on + # your network. + # config.vm.network "public_network" + + # Share an additional folder to the guest VM. The first argument is + # the path on the host to the actual folder. The second argument is + # the path on the guest to mount the folder. And the optional third + # argument is a set of non-required options. + # config.vm.synced_folder "../data", "/vagrant_data" + + # Provider-specific configuration so you can fine-tune various + # backing providers for Vagrant. These expose provider-specific options. + # Example for VirtualBox: + # + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + # end + # + # View the documentation for the provider you are using for more + # information on available options. + + # Enable provisioning with a shell script. Additional provisioners such as + # Ansible, Chef, Docker, Puppet and Salt are also available. Please see the + # documentation for more information about their specific syntax and use. + # config.vm.provision "shell", inline: <<-SHELL + # apt-get update + # apt-get install -y apache2 + # SHELL +end diff --git a/thirdparty/ryml/ext/c4core/.github/vagrant/vagrant-provision.sh b/thirdparty/ryml/ext/c4core/.github/vagrant/vagrant-provision.sh new file mode 100755 index 000000000..efa958738 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/vagrant/vagrant-provision.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +set -x + +# https://askubuntu.com/questions/735201/installing-clang-3-8-on-ubuntu-14-04-3 +wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add - + +done=$(grep C4STL /etc/apt/sources.list) +if [ -z "$done" ] ; then + cat >> /etc/apt/sources.list <<EOF + +# C4STL +# http://apt.llvm.org/ +#deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main +#deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.8 main +deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.9 main +deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-4.0 main +#deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-5.0 main +EOF +fi + +sudo -E apt-get install -y software-properties-common python-software-properties +sudo -E add-apt-repository -y ppa:ubuntu-toolchain-r/test +sudo -E add-apt-repository -y ppa:george-edison55/cmake-3.x +sudo -E apt-get -yq update + +sudo -E apt-get install -yq --force-yes \ + build-essential \ + cmake \ + g++-5 \ + g++-5-multilib \ + g++-6 \ + g++-6-multilib \ + g++-7 \ + g++-7-multilib \ + g++-8 \ + g++-8-multilib \ + g++-9 \ + g++-9-multilib \ + g++-10 \ + g++-10-multilib \ + g++-11 \ + g++-11-multilib \ + clang-3.7 \ + clang-3.8 \ + clang-3.9 \ + clang-4.0 \ + swig3.0 \ + libssl-dev \ + zlib1g-dev \ + libbz2-dev \ + libreadline-dev \ + libsqlite3-dev \ + wget \ + curl \ + llvm \ + libncurses5-dev \ + libncursesw5-dev \ + xz-utils \ + tk-dev \ + libffi-dev \ + liblzma-dev \ + python-openssl \ + git \ + python3 \ + python3-pip \ + python3-venv + +sudo -E pip install cmany + +exit 0 diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/arch.yml b/thirdparty/ryml/ext/c4core/.github/workflows/arch.yml new file mode 100644 index 000000000..67ebb32fb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/arch.yml @@ -0,0 +1,116 @@ +name: rarearchs + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + rarearchs: + name: ${{matrix.arch}}/c++${{matrix.std}}/${{matrix.bt}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + include: + - {std: 11, bt: Debug , arch: aarch64, distro: ubuntu20.04} + - {std: 11, bt: Release, arch: aarch64, distro: ubuntu20.04} + - {std: 14, bt: Debug , arch: aarch64, distro: ubuntu20.04} + - {std: 14, bt: Release, arch: aarch64, distro: ubuntu20.04} + - {std: 17, bt: Debug , arch: aarch64, distro: ubuntu20.04} + - {std: 17, bt: Release, arch: aarch64, distro: ubuntu20.04} + # + - {std: 11, bt: Debug , arch: ppc64le, distro: ubuntu20.04} + - {std: 11, bt: Release, arch: ppc64le, distro: ubuntu20.04} + - {std: 14, bt: Debug , arch: ppc64le, distro: ubuntu20.04} + - {std: 14, bt: Release, arch: ppc64le, distro: ubuntu20.04} + - {std: 17, bt: Debug , arch: ppc64le, distro: ubuntu20.04} + - {std: 17, bt: Release, arch: ppc64le, distro: ubuntu20.04} + # + - {std: 11, bt: Debug , arch: s390x , distro: ubuntu20.04} + - {std: 11, bt: Release, arch: s390x , distro: ubuntu20.04} + - {std: 14, bt: Debug , arch: s390x , distro: ubuntu20.04} + - {std: 14, bt: Release, arch: s390x , distro: ubuntu20.04} + - {std: 17, bt: Debug , arch: s390x , distro: ubuntu20.04} + - {std: 17, bt: Release, arch: s390x , distro: ubuntu20.04} + # + #- {std: 11, bt: Debug , arch: armv6 , distro: bullseye} + #- {std: 11, bt: Release, arch: armv6 , distro: bullseye} + #- {std: 14, bt: Debug , arch: armv6 , distro: bullseye} + #- {std: 14, bt: Release, arch: armv6 , distro: bullseye} + #- {std: 17, bt: Debug , arch: armv6 , distro: bullseye} + #- {std: 17, bt: Release, arch: armv6 , distro: bullseye} + # + #- {std: 11, bt: Debug , arch: armv7 , distro: ubuntu20.04} + #- {std: 11, bt: Release, arch: armv7 , distro: ubuntu20.04} + #- {std: 14, bt: Debug , arch: armv7 , distro: ubuntu20.04} + #- {std: 14, bt: Release, arch: armv7 , distro: ubuntu20.04} + #- {std: 17, bt: Debug , arch: armv7 , distro: ubuntu20.04} + #- {std: 17, bt: Release, arch: armv7 , distro: ubuntu20.04} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - name: test + uses: uraimo/[email protected] + with: + arch: ${{matrix.arch}} + distro: ${{matrix.distro}} + install: | + set -x + apt-get update -y + apt-get install -y \ + git \ + build-essential + # arm platforms need an up-to-date cmake: + # https://gitlab.kitware.com/cmake/cmake/-/issues/20568 + if [ "${{matrix.arch}}" == "armv6" ] || [ "${{matrix.arch}}" == "armv7" ] ; then + apt-get install -y \ + gpg \ + wget \ + apt-transport-https + wget --no-check-certificate -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null + echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ focal main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null + apt-get update -y + rm /usr/share/keyrings/kitware-archive-keyring.gpg + apt-get install kitware-archive-keyring + apt-get update -y + fi + apt-get install -y cmake cmake-data + cmake --version + run: | + set -x + uname -a + pwd + ls -lFhp . + # + bdir=build_${{matrix.arch}}_${{matrix.bt}}_${{matrix.std}} + idir=install_${{matrix.arch}}_${{matrix.bt}}_${{matrix.std}} + mkdir -p $bdir + # + cmake -S . -B $bdir \ + -DCMAKE_INSTALL_PREFIX=$idir \ + -DCMAKE_BUILD_TYPE=${{matrix.bt}} \ + -DC4_CXX_STANDARD=${{matrix.std}} \ + -DCXX_STANDARD=${{matrix.std}} \ + -DC4CORE_DEV=ON \ + -DC4CORE_BUILD_BENCHMARKS=OFF \ + -DC4CORE_SANITIZE=OFF \ + -DC4CORE_LINT=OFF \ + -DC4CORE_VALGRIND=OFF + # + cmake --build $bdir -j --target c4core-test-build + # + cmake --build $bdir --target c4core-test-run diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/benchmarks.yml b/thirdparty/ryml/ext/c4core/.github/workflows/benchmarks.yml new file mode 100644 index 000000000..acc7dd630 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/benchmarks.yml @@ -0,0 +1,250 @@ +name: benchmarks + +defaults: + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +jobs: + + gettag: + runs-on: ubuntu-latest + steps: + # use fetch-depth to ensure all tags are fetched + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive, fetch-depth: 0}} + - name: Variables (from tag) + if: contains(github.ref, 'tags/v') + run: | + # https://github.community/t/how-to-get-just-the-tag-name/16241/11 + SRC_TAG=${GITHUB_REF#refs/tags/} + SRC_VERSION=${GITHUB_REF#refs/tags/v} + cat <<EOF > vars.sh + export SRC_TAG=$SRC_TAG + export SRC_VERSION=$SRC_VERSION + EOF + - name: Variables (from commit, no tag) + if: ${{ !contains(github.ref, 'tags/v') }} + run: | + set -x + branch_name=${GITHUB_REF#refs/heads/} + # builds triggered from PRs have the branch_name like this: refs/pull/150/merge + # so filter to eg pr0150_merge + branch_name=`echo $branch_name | sed "s:refs/pull/\([0-9]*\)/\(.*\):pr0\1_\2:"` + # sanitize the branch name; eg merge/foo-bar -> merge_foo_bar + branch_name=`echo $branch_name | sed 's:[/.-]:_:g'` + git config --global --add safe.directory $(pwd) + SRC_TAG=$(git describe || git rev-parse --short HEAD) # eg v0.2.0-110-gda837e0 + SRC_VERSION="${branch_name}-${SRC_TAG}" + cat <<EOF > vars.sh + export SRC_TAG=$SRC_TAG + export SRC_VERSION=$SRC_VERSION + EOF + - name: Verify vars.sh + run: cat vars.sh ; source vars.sh ; echo $SRC_TAG ; echo $SRC_VERSION + - name: Save vars.sh + uses: actions/upload-artifact@v3 + with: {name: vars.sh, path: ./vars.sh} + + bm_x86_64: + name: bm/x86_64/c++${{matrix.std}}/${{matrix.cxx}}/${{matrix.bt}} + needs: gettag + if: | + (!contains(github.event.head_commit.message, 'skip all')) || + (!contains(github.event.head_commit.message, 'skip benchmarks')) || + contains(github.event.head_commit.message, 'only benchmarks') + continue-on-error: true + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {std: 17, cxx: g++-10, bt: Release, os: ubuntu-20.04, bitlinks: static64 static32} + - {std: 20, cxx: g++-10, bt: Release, os: ubuntu-20.04, bitlinks: static64 static32} + # + - {std: 17, cxx: vs2019, bt: Release, os: windows-2019, bitlinks: static64 static32} + - {std: 20, cxx: vs2019, bt: Release, os: windows-2019, bitlinks: static64 static32} + - {std: 17, cxx: vs2022, bt: Release, os: windows-2022, bitlinks: static64 static32} + - {std: 20, cxx: vs2022, bt: Release, os: windows-2022, bitlinks: static64 static32} + # + - {std: 17, cxx: xcode, xcver: 13, bt: Release, os: macos-11, bitlinks: static64} + env: {BM: ON, STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + # use fetch-depth to ensure all tags are fetched + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive, fetch-depth: 0}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - name: Download vars.sh + uses: actions/download-artifact@v3 + with: {name: vars.sh, path: ./} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: Install python 3.10 for plotting + uses: actions/setup-python@v4 + with: { python-version: '3.10' } + - name: install benchmark plotting dependencies + run: | + which python + which pip + python --version + pip --version + pip install -v -r cmake/bm-xp/requirements.txt + python -c 'import munch ; print("ok!") ; exit(0)' + echo $? + - name: shared64-configure--------------------------------------------------- + run: export CMAKE_FLAGS="-DPython_EXECUTABLE=$(which python)" && source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_target shared64 c4core-bm-build} + - {name: shared64-run, run: export NUM_JOBS_BUILD=1 && source .github/setenv.sh && c4_run_target shared64 c4core-bm-run} + - {name: shared64-plot, run: export NUM_JOBS_BUILD=1 && source .github/setenv.sh && c4_run_target shared64 c4core-bm-plot} + - name: static64-configure--------------------------------------------------- + run: export CMAKE_FLAGS="-DPython_EXECUTABLE=$(which python)" && source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_target static64 c4core-bm-build} + - {name: static64-run, run: export NUM_JOBS_BUILD=1 && source .github/setenv.sh && c4_run_target static64 c4core-bm-run} + - {name: static64-plot, run: export NUM_JOBS_BUILD=1 && source .github/setenv.sh && c4_run_target static64 c4core-bm-plot} + - name: static32-configure--------------------------------------------------- + run: export CMAKE_FLAGS="-DPython_EXECUTABLE=$(which python)" && source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_target static32 c4core-bm-build} + - {name: static32-run, run: export NUM_JOBS_BUILD=1 && source .github/setenv.sh && c4_run_target static32 c4core-bm-run} + - {name: static32-plot, run: export NUM_JOBS_BUILD=1 && source .github/setenv.sh && c4_run_target static32 c4core-bm-plot} + - name: shared32-configure--------------------------------------------------- + run: export CMAKE_FLAGS="-DPython_EXECUTABLE=$(which python)" && source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_target shared32 c4core-bm-build} + - {name: shared32-run, run: export NUM_JOBS_BUILD=1 && source .github/setenv.sh && c4_run_target shared32 c4core-bm-run} + - {name: shared32-plot, run: export NUM_JOBS_BUILD=1 && source .github/setenv.sh && c4_run_target shared32 c4core-bm-plot} + - name: gather benchmark results + run: | + set -x + source vars.sh + echo SRC_TAG=$SRC_TAG + echo SRC_VERSION=$SRC_VERSION + desc=$SRC_TAG + for bl in ${{matrix.bitlinks}} ; do + dst=$(echo benchmark_results/$desc/x86_64/${{matrix.cxx}}-${{matrix.bt}}-c++${{matrix.std}}-$bl | sed 's:++-:xx:g' | sed 's:+:x:g') + mkdir -p $dst + find build -name bm-results + mv -vf build/$bl/bm/bm-results/* $dst/. + done + - name: upload benchmark result artifacts + uses: actions/upload-artifact@v3 + with: + name: benchmark_results + path: benchmark_results/ + + #-------------------------------------------------------------------------------------------------- + bm_rarearch: + name: bm/${{matrix.arch}}/c++${{matrix.std}}/${{matrix.bt}} + needs: gettag + if: | + (!contains(github.event.head_commit.message, 'skip all')) || + (!contains(github.event.head_commit.message, 'skip benchmarks')) || + contains(github.event.head_commit.message, 'only benchmarks') + continue-on-error: true + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + include: + - {std: 17, bt: Release, arch: aarch64, distro: ubuntu20.04} + # the python dependencies cannot be installed for this one: + #- {std: 17, bt: Release, arch: ppc64le, distro: ubuntu20.04} + # + # the github runners are failing for the following: + #- {std: 11, bt: Release, arch: s390x , distro: ubuntu20.04} + #- {std: 17, bt: Release, arch: s390x , distro: ubuntu20.04} + ## + #- {std: 11, bt: Release, arch: armv6 , distro: ubuntu18.04} + #- {std: 17, bt: Release, arch: armv6 , distro: ubuntu18.04} + ## + #- {std: 11, bt: Release, arch: armv7 , distro: ubuntu18.04} + #- {std: 17, bt: Release, arch: armv7 , distro: ubuntu18.04} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive, fetch-depth: 0}} + - name: Download vars.sh + uses: actions/download-artifact@v3 + with: {name: vars.sh, path: ./} + - name: test + uses: uraimo/[email protected] + with: + arch: ${{matrix.arch}} + distro: ${{matrix.distro}} + install: | + set -x + apt-get update -y + apt-get install -y \ + git \ + build-essential + # arm platforms need an up-to-date cmake: + # https://gitlab.kitware.com/cmake/cmake/-/issues/20568 + if [ "${{matrix.arch}}" == "armv6" ] || [ "${{matrix.arch}}" == "armv7" ] ; then + apt-get install -y \ + gpg \ + wget \ + apt-transport-https + wget --no-check-certificate -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null + echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ focal main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null + apt-get update -y + rm /usr/share/keyrings/kitware-archive-keyring.gpg + apt-get install kitware-archive-keyring + apt-get update -y + fi + apt-get install -y cmake cmake-data + cmake --version + apt-get install -y python3 python3-pip + run: | + set -x + uname -a + pwd + ls -lFhp . + # + pip3 install -v -r cmake/bm-xp/requirements.txt + # + bdir=build_${{matrix.arch}}_${{matrix.bt}}_${{matrix.std}} + idir=install_${{matrix.arch}}_${{matrix.bt}}_${{matrix.std}} + mkdir -p $bdir + # + cmake -S . -B $bdir \ + -DCMAKE_INSTALL_PREFIX=$idir \ + -DCMAKE_BUILD_TYPE=${{matrix.bt}} \ + -DC4_CXX_STANDARD=${{matrix.std}} \ + -DCXX_STANDARD=${{matrix.std}} \ + -DC4CORE_DEV=ON \ + -DC4CORE_BUILD_TESTS=OFF \ + -DC4CORE_BUILD_BENCHMARKS=ON \ + -DC4CORE_SANITIZE=OFF \ + -DC4CORE_LINT=OFF \ + -DC4CORE_VALGRIND=OFF + # + cmake --build $bdir -j --target c4core-bm-build + # + cmake --build $bdir -j 1 --target c4core-bm-run + # + cmake --build $bdir -j 1 --target c4core-bm-plot + # + source vars.sh + echo SRC_TAG=$SRC_TAG + echo SRC_VERSION=$SRC_VERSION + desc=$SRC_TAG + dst=$(echo benchmark_results/$desc/${{matrix.arch}}/${{matrix.bt}}-c++${{matrix.std}} | sed 's:++-:xx:g' | sed 's:+:x:g') + mkdir -p $dst + find $bdir -name bm-results + mv -vf $bdir/bm/bm-results/* $dst/. + - name: upload benchmark result artifacts + uses: actions/upload-artifact@v3 + with: + name: benchmark_results + path: benchmark_results/ diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/clang.yml b/thirdparty/ryml/ext/c4core/.github/workflows/clang.yml new file mode 100644 index 000000000..df2194885 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/clang.yml @@ -0,0 +1,230 @@ +name: clang + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +# ubuntu-20.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.2.0 +# clang: 8.0.1, 9.0.1, 10.0.0 +# ubuntu-18.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.1.0 +# clang: 6.0.0, 8.0.0, 9.0.0 +# macos-11.0: macOS Big Sur 11.0 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 10.0.1 +# gcc-8 gcc-9 +# macos-10.15: macOS Catalina 10.15 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 11.0.0 +# gcc-8 gcc-9 +# windows-2019: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md +# vs2019 +# windows-2016: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md +# vs2017 + +jobs: + + #---------------------------------------------------------------------------- + clang_canary: + name: clang_canary/${{matrix.cxx}}/c++${{matrix.std}}/${{matrix.bt}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {std: 20, cxx: clang++-10 , bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static32} + - {std: 20, cxx: clang++-10 , bt: Release, os: ubuntu-20.04, bitlinks: shared64 static32} + - {std: 11, cxx: clang++-6.0, bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static32} + - {std: 11, cxx: clang++-6.0, bt: Release, os: ubuntu-20.04, bitlinks: shared64 static32} + env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} + - {name: shared64-run, run: source .github/setenv.sh && c4_run_test shared64} + - {name: shared64-pack, run: source .github/setenv.sh && c4_package shared64} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_run_test static64} + - {name: static64-pack, run: source .github/setenv.sh && c4_package static64} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} + - {name: static32-pack, run: source .github/setenv.sh && c4_package static32} + - name: shared32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_test shared32} + - {name: shared32-run, run: source .github/setenv.sh && c4_run_test shared32} + - {name: shared32-pack, run: source .github/setenv.sh && c4_package shared32} + + #---------------------------------------------------------------------------- + clang_extended: + name: clang_extended/${{matrix.cxx}}/c++${{matrix.std}}/${{matrix.bt}}/vg${{matrix.vg}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {std: 20, cxx: clang++-10 , bt: Debug , vg: on, os: ubuntu-18.04} + - {std: 20, cxx: clang++-10 , bt: Release, vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-9 , bt: Debug , vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-9 , bt: Release, vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-8 , bt: Debug , vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-8 , bt: Release, vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-7 , bt: Debug , vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-7 , bt: Release, vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-6.0, bt: Debug , vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-6.0, bt: Release, vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-5.0, bt: Debug , vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-5.0, bt: Release, vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-4.0, bt: Debug , vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-4.0, bt: Release, vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-3.9, bt: Debug , vg: on, os: ubuntu-18.04} + - {std: 11, cxx: clang++-3.9, bt: Release, vg: on, os: ubuntu-18.04} + env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} + - {name: shared64-run, run: source .github/setenv.sh && c4_run_test shared64} + - {name: shared64-pack, run: source .github/setenv.sh && c4_package shared64} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_run_test static64} + - {name: static64-pack, run: source .github/setenv.sh && c4_package static64} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} + - {name: static32-pack, run: source .github/setenv.sh && c4_package static32} + - name: shared32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_test shared32} + - {name: shared32-run, run: source .github/setenv.sh && c4_run_test shared32} + - {name: shared32-pack, run: source .github/setenv.sh && c4_package shared32} + + #---------------------------------------------------------------------------- + clang_sanitize: + name: clang_sanitize/c++${{matrix.std}}/${{matrix.bt}}/vg${{matrix.vg}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + # these jobs take much longer, so run only one bitlink pair per job to profit from parallelism + - {std: 11, cxx: clang++-10 , bt: Debug , vg: ON, san: ALL, bitlinks: shared64 static64, os: ubuntu-18.04} + - {std: 11, cxx: clang++-10 , bt: Debug , vg: ON, san: ALL, bitlinks: shared32 static32, os: ubuntu-18.04} + - {std: 11, cxx: clang++-10 , bt: Release, vg: ON, san: ALL, bitlinks: shared64 static64, os: ubuntu-18.04} + - {std: 11, cxx: clang++-10 , bt: Release, vg: ON, san: ALL, bitlinks: shared32 static32, os: ubuntu-18.04} + - {std: 14, cxx: clang++-10 , bt: Debug , vg: ON, san: ALL, bitlinks: shared64 static64, os: ubuntu-18.04} + - {std: 14, cxx: clang++-10 , bt: Debug , vg: ON, san: ALL, bitlinks: shared32 static32, os: ubuntu-18.04} + - {std: 14, cxx: clang++-10 , bt: Release, vg: ON, san: ALL, bitlinks: shared64 static64, os: ubuntu-18.04} + - {std: 14, cxx: clang++-10 , bt: Release, vg: ON, san: ALL, bitlinks: shared32 static32, os: ubuntu-18.04} + - {std: 17, cxx: clang++-10 , bt: Debug , vg: ON, san: ALL, bitlinks: shared64 static64, os: ubuntu-18.04} + - {std: 17, cxx: clang++-10 , bt: Debug , vg: ON, san: ALL, bitlinks: shared32 static32, os: ubuntu-18.04} + - {std: 17, cxx: clang++-10 , bt: Release, vg: ON, san: ALL, bitlinks: shared64 static64, os: ubuntu-18.04} + - {std: 17, cxx: clang++-10 , bt: Release, vg: ON, san: ALL, bitlinks: shared32 static32, os: ubuntu-18.04} + - {std: 20, cxx: clang++-10 , bt: Debug , vg: ON, san: ALL, bitlinks: shared64 static64, os: ubuntu-18.04} + - {std: 20, cxx: clang++-10 , bt: Debug , vg: ON, san: ALL, bitlinks: shared32 static32, os: ubuntu-18.04} + - {std: 20, cxx: clang++-10 , bt: Release, vg: ON, san: ALL, bitlinks: shared64 static64, os: ubuntu-18.04} + - {std: 20, cxx: clang++-10 , bt: Release, vg: ON, san: ALL, bitlinks: shared32 static32, os: ubuntu-18.04} + env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} + - {name: shared64-run, run: source .github/setenv.sh && c4_run_test shared64} + - {name: shared64-pack, run: source .github/setenv.sh && c4_package shared64} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_run_test static64} + - {name: static64-pack, run: source .github/setenv.sh && c4_package static64} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} + - {name: static32-pack, run: source .github/setenv.sh && c4_package static32} + - name: shared32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_test shared32} + - {name: shared32-run, run: source .github/setenv.sh && c4_run_test shared32} + - {name: shared32-pack, run: source .github/setenv.sh && c4_package shared32} + + #---------------------------------------------------------------------------- +# # https://blog.kitware.com/static-checks-with-cmake-cdash-iwyu-clang-tidy-lwyu-cpplint-and-cppcheck/ +# static_analysis: +# continue-on-error: true +# if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct +# runs-on: ${{matrix.os}} +# strategy: +# fail-fast: false +# matrix: +# include: +# # these jobs take much longer, so run only one bitlink pair per job to profit from parallelism +# - {std: 11, cxx: clang++-10, bt: Debug , bitlinks: shared64, os: ubuntu-18.04} +# - {std: 11, cxx: clang++-10, bt: Release, bitlinks: shared64, os: ubuntu-18.04} +# - {std: 14, cxx: clang++-10, bt: Debug , bitlinks: shared64, os: ubuntu-18.04} +# - {std: 14, cxx: clang++-10, bt: Release, bitlinks: shared64, os: ubuntu-18.04} +# - {std: 17, cxx: clang++-10, bt: Debug , bitlinks: shared64, os: ubuntu-18.04} +# - {std: 17, cxx: clang++-10, bt: Release, bitlinks: shared64, os: ubuntu-18.04} +# - {std: 20, cxx: clang++-10, bt: Debug , bitlinks: shared64, os: ubuntu-18.04} +# - {std: 20, cxx: clang++-10, bt: Release, bitlinks: shared64, os: ubuntu-18.04} +# env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} +# steps: +# - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} +# - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} +# - {name: show info, run: source .github/setenv.sh && c4_show_info} +# - name: shared64-configure--------------------------------------------------- +# run: source .github/setenv.sh && c4_cfg_test shared64 +# - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} +# - {name: clang-tidy, run: cmake "-DCMAKE_CXX_CLANG_TIDY=/usr/bin/clang-tidy-3.9;-checks=*" ../path/to/source} +# - {name: cppcheck, run: cmake "-DCMAKE_CXX_CPPCHECK=/usr/bin/cppcheck;--std=c++11" ../path/to/source} +# - {name: cpplint, run: cmake "-DCMAKE_CXX_CPPLINT=/usr/local/bin/cpplint;--linelength=179" ..} +# - {name: include-what-you-use, run: cmake "-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=/usr/bin/iwyu;--transitive_includes_only" ..} +# - {name: link-what-you-use, run: cmake -DCMAKE_LINK_WHAT_YOU_USE=TRUE ..} diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/clang_tidy.yml b/thirdparty/ryml/ext/c4core/.github/workflows/clang_tidy.yml new file mode 100644 index 000000000..9d452ec4c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/clang_tidy.yml @@ -0,0 +1,97 @@ +name: clang_tidy + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +# ubuntu-20.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.2.0 +# clang: 8.0.1, 9.0.1, 10.0.0 +# ubuntu-18.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.1.0 +# clang: 6.0.0, 8.0.0, 9.0.0 +# macos-11.0: macOS Big Sur 11.0 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 10.0.1 +# gcc-8 gcc-9 +# macos-10.15: macOS Catalina 10.15 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 11.0.0 +# gcc-8 gcc-9 +# windows-2019: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md +# vs2019 +# windows-2016: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md +# vs2017 + +jobs: + + #---------------------------------------------------------------------------- + clang_tidy: + name: clang_tidy/c++${{matrix.std}}/${{matrix.bt}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + # clang tidy takes a long time, so don't do multiple bits/linktypes + - {std: 11, cxx: clang++-9, bt: Debug , lint: clang-tidy, bitlinks: shared64, os: ubuntu-20.04} + - {std: 11, cxx: clang++-9, bt: Debug , lint: clang-tidy, bitlinks: shared32, os: ubuntu-20.04} + - {std: 11, cxx: clang++-9, bt: Debug , lint: clang-tidy, bitlinks: static64, os: ubuntu-20.04} + - {std: 11, cxx: clang++-9, bt: Debug , lint: clang-tidy, bitlinks: static32, os: ubuntu-20.04} + - {std: 11, cxx: clang++-9, bt: ReleaseWithDebInfo, lint: clang-tidy, bitlinks: shared64, os: ubuntu-20.04} + - {std: 11, cxx: clang++-9, bt: ReleaseWithDebInfo, lint: clang-tidy, bitlinks: shared32, os: ubuntu-20.04} + - {std: 11, cxx: clang++-9, bt: ReleaseWithDebInfo, lint: clang-tidy, bitlinks: static64, os: ubuntu-20.04} + - {std: 11, cxx: clang++-9, bt: ReleaseWithDebInfo, lint: clang-tidy, bitlinks: static32, os: ubuntu-20.04} + env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} + - {name: shared64-run, run: source .github/setenv.sh && c4_run_test shared64} + - {name: shared64-pack, run: source .github/setenv.sh && c4_package shared64} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_run_test static64} + - {name: static64-pack, run: source .github/setenv.sh && c4_package static64} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} + - {name: static32-pack, run: source .github/setenv.sh && c4_package static32} + - name: shared32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_test shared32} + - {name: shared32-run, run: source .github/setenv.sh && c4_run_test shared32} + - {name: shared32-pack, run: source .github/setenv.sh && c4_package shared32} diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/codeql.yml b/thirdparty/ryml/ext/c4core/.github/workflows/codeql.yml new file mode 100644 index 000000000..ca8d78bb7 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/codeql.yml @@ -0,0 +1,43 @@ +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: "59 18 * * 1" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ cpp ] + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + queries: +security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{ matrix.language }}" diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/coverage.yml b/thirdparty/ryml/ext/c4core/.github/workflows/coverage.yml new file mode 100644 index 000000000..6f593de14 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/coverage.yml @@ -0,0 +1,150 @@ +name: coverage + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +jobs: + + #---------------------------------------------------------------------------- + coverage: + name: coverage/${{matrix.name}} + # if: github.ref == 'refs/heads/master' + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {name: c++11, std: 11, cxx: g++-9, cc: gcc-9, bt: Coverage, os: ubuntu-20.04} + - {name: c++17, std: 17, cxx: g++-9, cc: gcc-9, bt: Coverage, os: ubuntu-20.04} + #- {name: c++20, std: 20, cxx: g++-9, cc: gcc-9, bt: Coverage, os: ubuntu-20.04} + env: { + STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}", + CODECOV_TOKEN: "${{secrets.CODECOV_TOKEN}}", + COVERALLS_REPO_TOKEN: "${{secrets.COVERALLS_REPO_TOKEN}}", + COVERALLS_PARALLEL: true, + } + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_build_target static64 c4core-coverage} + - name: static64-coverage-artifacts + uses: actions/upload-artifact@v3 + with: + name: coverage-static64 + path: | + build/static64/lcov/ + build/static64/coverage3-final_filtered.lcov + - {name: static64-submit-codecov, run: source .github/setenv.sh && c4_submit_coverage static64 codecov} + - {name: static64-submit-coveralls, run: source .github/setenv.sh && c4_submit_coverage static64 coveralls, + env: {COVERALLS_FLAG_NAME: "${{matrix.name}}/static64"}} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_build_target static32 c4core-coverage} + - name: static32-coverage-artifacts + uses: actions/upload-artifact@v3 + with: + name: coverage-static32 + path: | + build/static32/lcov/ + build/static32/coverage3-final_filtered.lcov + - {name: static32-submit-codecov, run: source .github/setenv.sh && c4_submit_coverage static32 codecov} + - {name: static32-submit-coveralls, run: source .github/setenv.sh && c4_submit_coverage static32 coveralls, + env: {COVERALLS_FLAG_NAME: "${{matrix.name}}/static32"}} + + #---------------------------------------------------------------------------- + coverage_nofastfloat: + name: coverage/${{matrix.name}} + # if: github.ref == 'refs/heads/master' + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {name: nofastfloat/c++11, std: 11, cxx: g++-9, cc: gcc-9, bt: Coverage, os: ubuntu-20.04} + - {name: nofastfloat/c++17, std: 17, cxx: g++-9, cc: gcc-9, bt: Coverage, os: ubuntu-20.04} + env: { + STD: "${{matrix.std}}", + CXX_: "${{matrix.cxx}}", + BT: "${{matrix.bt}}", + OS: "${{matrix.os}}", + CODECOV_TOKEN: "${{secrets.CODECOV_TOKEN}}", + COVERALLS_REPO_TOKEN: "${{secrets.COVERALLS_REPO_TOKEN}}", + COVERALLS_PARALLEL: true, # https://docs.coveralls.io/parallel-build-webhook + COVERALLS_FLAG_NAME: "${{matrix.name}}", + BDIR: "build/nofastfloat-${{matrix.cxx}}-cxx${{matrix.std}}", + IDIR: "install/nofastfloat-${{matrix.cxx}}-cxx${{matrix.std}}", + } + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: nofastfloat-configure------------------------------------------------ + run: | + set -x + mkdir -p $BDIR + mkdir -p $IDIR + cmake -S . -B $BDIR \ + -DC4CORE_WITH_FASTFLOAT=OFF \ + -DC4_CXX_STANDARD=${{matrix.std}} \ + -DC4CORE_CXX_STANDARD=${{matrix.std}} \ + -DC4CORE_BUILD_TESTS=ON \ + -DC4CORE_VALGRIND=OFF \ + -DC4CORE_COVERAGE_CODECOV=ON \ + -DC4CORE_COVERAGE_COVERALLS=ON \ + -DCMAKE_INSTALL_PREFIX=$IDIR \ + -DCMAKE_BUILD_TYPE=Coverage \ + -DCMAKE_CXX_COMPILER=${{matrix.cxx}} \ + -DCMAKE_C_COMPILER=${{matrix.cc}} + - {name: nofastfloat-build, run: cmake --build $BDIR --config Coverage --target c4core-test-build -j} + - {name: nofastfloat-run, run: cmake --build $BDIR --config Coverage --target c4core-coverage} + - name: nofastfloat-coverage-artifacts + uses: actions/upload-artifact@v3 + with: + name: coverage-shared32 + path: | + build/nofastfloat-${{matrix.cxx}}-cxx${{matrix.std}}/lcov/ + build/nofastfloat-${{matrix.cxx}}-cxx${{matrix.std}}/coverage3-final_filtered.lcov + - {name: nofastfloat-submit-codecov, run: cmake --build $BDIR --config Coverage --target c4core-coverage-submit-codecov} + - {name: nofastfloat-submit-coveralls, run: cmake --build $BDIR --config Coverage --target c4core-coverage-submit-coveralls} + + # https://github.com/marketplace/actions/coveralls-github-action + coveralls_finish: + needs: [coverage, coverage_nofastfloat] + runs-on: ubuntu-latest + steps: + - name: coveralls-notify + continue-on-error: true + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.github_token }} + parallel-finished: true diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/emscripten.yml b/thirdparty/ryml/ext/c4core/.github/workflows/emscripten.yml new file mode 100644 index 000000000..cabe5d293 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/emscripten.yml @@ -0,0 +1,99 @@ +name: emscripten + +# running in a local machine: +# /usr/lib/emscripten/emcmake cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build/emscripten -DC4CORE_DEV=ON -DC4CORE_SANITIZE=OFF -DC4CORE_BUILD_BENCHMARKS=OFF -DCMAKE_CXX_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0" && cmake --build build/emscripten/ -j --target c4core-test-charconv && ( cd build/emscripten/test/ && ctest --output-on-failure -R charconv ) + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +# ubuntu-20.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.2.0 +# clang: 8.0.1, 9.0.1, 10.0.0 +# ubuntu-18.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.1.0 +# clang: 6.0.0, 8.0.0, 9.0.0 +# macos-11.0: macOS Big Sur 11.0 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 10.0.1 +# gcc-8 gcc-9 +# macos-10.15: macOS Catalina 10.15 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 11.0.0 +# gcc-8 gcc-9 +# windows-2019: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md +# vs2019 +# windows-2016: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md +# vs2017 + +jobs: + + #---------------------------------------------------------------------------- + emscripten: + name: emscripten/${{matrix.emver}}/c++${{matrix.std}}/${{matrix.bt}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + #- {std: 11, cxx: em++, emver: 2.0.34, bt: Debug , os: ubuntu-latest, bitlinks: static32} + - {std: 11, cxx: em++, emver: 2.0.34, bt: Release, os: ubuntu-latest, bitlinks: static32} + #- {std: 20, cxx: em++, emver: 2.0.34, bt: Debug , os: ubuntu-latest, bitlinks: static32} + - {std: 20, cxx: em++, emver: 2.0.34, bt: Release, os: ubuntu-latest, bitlinks: static32} + #- {std: 11, cxx: em++, emver: 3.0.0 , bt: Debug , os: ubuntu-latest, bitlinks: static32} + - {std: 11, cxx: em++, emver: 3.0.0 , bt: Release, os: ubuntu-latest, bitlinks: static32} + #- {std: 20, cxx: em++, emver: 3.0.0 , bt: Debug , os: ubuntu-latest, bitlinks: static32} + - {std: 20, cxx: em++, emver: 3.0.0 , bt: Release, os: ubuntu-latest, bitlinks: static32} + env: + STD: "${{matrix.std}}" + CXX_: "${{matrix.cxx}}" + BT: "${{matrix.bt}}" + BITLINKS: "${{matrix.bitlinks}}" + VG: "${{matrix.vg}}" + SAN: "${{matrix.san}}" + LINT: "${{matrix.lint}}" + OS: "${{matrix.os}}" + EM_VERSION: "${{matrix.emver}}" + EM_CACHE_FOLDER: 'emsdk-cache' + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - name: setup emscripten cache + id: cache-system-libraries + uses: actions/cache@v3 + with: {path: "${{env.EM_CACHE_FOLDER}}", key: "${{env.EM_VERSION}}-${{runner.os}}"} + - name: setup emscripten + uses: mymindstorm/setup-emsdk@v11 + with: {version: "${{matrix.emver}}", actions-cache-folder: "${{env.EM_CACHE_FOLDER}}"} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/gcc.yml b/thirdparty/ryml/ext/c4core/.github/workflows/gcc.yml new file mode 100644 index 000000000..5398d5f22 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/gcc.yml @@ -0,0 +1,181 @@ +name: gcc + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +# ubuntu-20.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.2.0 +# clang: 8.0.1, 9.0.1, 10.0.0 +# ubuntu-18.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.1.0 +# clang: 6.0.0, 8.0.0, 9.0.0 +# macos-11.0: macOS Big Sur 11.0 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 10.0.1 +# gcc-8 gcc-9 +# macos-10.15: macOS Catalina 10.15 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 11.0.0 +# gcc-8 gcc-9 +# windows-2019: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md +# vs2019 +# windows-2016: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md +# vs2017 + +jobs: + + #---------------------------------------------------------------------------- + gcc_canary: + name: gcc_canary/${{matrix.cxx}}/c++${{matrix.std}}/${{matrix.bt}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {std: 11, cxx: g++-7 , bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static32} + - {std: 11, cxx: g++-7 , bt: Release, os: ubuntu-20.04, bitlinks: shared64 static32} + - {std: 20, cxx: g++-10 , bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static32} + - {std: 20, cxx: g++-10 , bt: Release, os: ubuntu-20.04, bitlinks: shared64 static32} + - {std: 11, cxx: g++-5 , bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static32} + - {std: 11, cxx: g++-5 , bt: Release, os: ubuntu-20.04, bitlinks: shared64 static32} + - {std: 11, cxx: g++-4.8 , bt: Debug, os: ubuntu-18.04, bitlinks: shared64 static32} + - {std: 11, cxx: g++-4.8 , bt: Release, os: ubuntu-18.04, bitlinks: shared64 static32} + env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} + - {name: shared64-run, run: source .github/setenv.sh && c4_run_test shared64} + - {name: shared64-pack, run: source .github/setenv.sh && c4_package shared64} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_run_test static64} + - {name: static64-pack, run: source .github/setenv.sh && c4_package static64} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} + - {name: static32-pack, run: source .github/setenv.sh && c4_package static32} + - name: shared32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_test shared32} + - {name: shared32-run, run: source .github/setenv.sh && c4_run_test shared32} + - {name: shared32-pack, run: source .github/setenv.sh && c4_package shared32} + + #---------------------------------------------------------------------------- + gcc_extended: + name: gcc_extended/${{matrix.cxx}}/c++${{matrix.std}}/${{matrix.bt}}/vg${{matrix.vg}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + # VALGRIND + - {std: 11, cxx: g++-10, bt: Debug , vg: ON, os: ubuntu-20.04} + - {std: 11, cxx: g++-10, bt: Release, vg: ON, os: ubuntu-20.04} + - {std: 14, cxx: g++-10, bt: Debug , vg: ON, os: ubuntu-20.04} + - {std: 14, cxx: g++-10, bt: Release, vg: ON, os: ubuntu-20.04} + - {std: 17, cxx: g++-10, bt: Debug , vg: ON, os: ubuntu-20.04} + - {std: 17, cxx: g++-10, bt: Release, vg: ON, os: ubuntu-20.04} + - {std: 20, cxx: g++-10, bt: Debug , vg: ON, os: ubuntu-20.04} + - {std: 20, cxx: g++-10, bt: Release, vg: ON, os: ubuntu-20.04} + # + - {std: 11, cxx: g++-9, bt: Debug , os: ubuntu-20.04} + - {std: 11, cxx: g++-9, bt: Release, os: ubuntu-20.04} + - {std: 11, cxx: g++-8, bt: Debug , os: ubuntu-20.04} + - {std: 11, cxx: g++-8, bt: Release, os: ubuntu-20.04} + - {std: 11, cxx: g++-7, bt: Debug , os: ubuntu-20.04} + - {std: 11, cxx: g++-7, bt: Release, os: ubuntu-20.04} + - {std: 11, cxx: g++-6, bt: Debug , os: ubuntu-18.04} + - {std: 11, cxx: g++-6, bt: Release, os: ubuntu-18.04} + - {std: 11, cxx: g++-5, bt: Debug , os: ubuntu-18.04} + - {std: 11, cxx: g++-5, bt: Release, os: ubuntu-18.04} + - {std: 11, cxx: g++-4.8, bt: Debug, os: ubuntu-18.04} + - {std: 11, cxx: g++-4.8, bt: Release, os: ubuntu-18.04} + env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} + - {name: shared64-run, run: source .github/setenv.sh && c4_run_test shared64} + - {name: shared64-pack, run: source .github/setenv.sh && c4_package shared64} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_run_test static64} + - {name: static64-pack, run: source .github/setenv.sh && c4_package static64} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} + - {name: static32-pack, run: source .github/setenv.sh && c4_package static32} + - name: shared32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_test shared32} + - {name: shared32-run, run: source .github/setenv.sh && c4_run_test shared32} + - {name: shared32-pack, run: source .github/setenv.sh && c4_package shared32} + + #---------------------------------------------------------------------------- + arm: + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + # these jobs take much longer, so run only one bitlink pair per job to profit from parallelism + - {std: 11, bt: Debug , toolchain: cmake/Toolchain-Arm-ubuntu.cmake, cxx: arm-linux-gnueabihf-gcc, os: ubuntu-18.04} + - {std: 11, bt: Release, toolchain: cmake/Toolchain-Arm-ubuntu.cmake, cxx: arm-linux-gnueabihf-gcc, os: ubuntu-18.04} + - {std: 14, bt: Debug , toolchain: cmake/Toolchain-Arm-ubuntu.cmake, cxx: arm-linux-gnueabihf-gcc, os: ubuntu-18.04} + - {std: 14, bt: Release, toolchain: cmake/Toolchain-Arm-ubuntu.cmake, cxx: arm-linux-gnueabihf-gcc, os: ubuntu-18.04} + - {std: 17, bt: Debug , toolchain: cmake/Toolchain-Arm-ubuntu.cmake, cxx: arm-linux-gnueabihf-gcc, os: ubuntu-18.04} + - {std: 17, bt: Release, toolchain: cmake/Toolchain-Arm-ubuntu.cmake, cxx: arm-linux-gnueabihf-gcc, os: ubuntu-18.04} + env: {TOOLCHAIN: "${{matrix.toolchain}}", STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test arm + - {name: build, run: source .github/setenv.sh && c4_build_test arm} + - {name: run, run: source .github/setenv.sh && c4_run_test arm} + - {name: pack, run: source .github/setenv.sh && c4_package arm} diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/libcxx.yml b/thirdparty/ryml/ext/c4core/.github/workflows/libcxx.yml new file mode 100644 index 000000000..f55fe6adc --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/libcxx.yml @@ -0,0 +1,111 @@ +name: libcxx + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +# ubuntu-20.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.2.0 +# clang: 8.0.1, 9.0.1, 10.0.0 +# ubuntu-18.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.1.0 +# clang: 6.0.0, 8.0.0, 9.0.0 +# macos-11.0: macOS Big Sur 11.0 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 10.0.1 +# gcc-8 gcc-9 +# macos-10.15: macOS Catalina 10.15 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 11.0.0 +# gcc-8 gcc-9 +# windows-2019: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md +# vs2019 +# windows-2016: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md +# vs2017 + +jobs: + + #---------------------------------------------------------------------------- + libcxx: + name: libc++/${{matrix.cxx}}/c++${{matrix.std}}/${{matrix.bt}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {std: 20, cxx: clang++-10 , bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 20, cxx: clang++-10 , bt: Release, os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 17, cxx: clang++-10 , bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 17, cxx: clang++-10 , bt: Release, os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 14, cxx: clang++-10 , bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 14, cxx: clang++-10 , bt: Release, os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 11, cxx: clang++-10 , bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 11, cxx: clang++-10 , bt: Release, os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 17, cxx: clang++-6.0, bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 17, cxx: clang++-6.0, bt: Release, os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 14, cxx: clang++-6.0, bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 14, cxx: clang++-6.0, bt: Release, os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 11, cxx: clang++-6.0, bt: Debug , os: ubuntu-20.04, bitlinks: shared64 static64} + - {std: 11, cxx: clang++-6.0, bt: Release, os: ubuntu-20.04, bitlinks: shared64 static64} + env: + LIBCXX: ON # <---- enable libc++ + STD: "${{matrix.std}}" + CXX_: "${{matrix.cxx}}" + BT: "${{matrix.bt}}" + BITLINKS: "${{matrix.bitlinks}}" + VG: "${{matrix.vg}}" + SAN: "${{matrix.san}}" + LINT: "${{matrix.lint}}" + OS: "${{matrix.os}}" + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} + - {name: shared64-run, run: source .github/setenv.sh && c4_run_test shared64} + - {name: shared64-pack, run: source .github/setenv.sh && c4_package shared64} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_run_test static64} + - {name: static64-pack, run: source .github/setenv.sh && c4_package static64} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} + - {name: static32-pack, run: source .github/setenv.sh && c4_package static32} + - name: shared32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_test shared32} + - {name: shared32-run, run: source .github/setenv.sh && c4_run_test shared32} + - {name: shared32-pack, run: source .github/setenv.sh && c4_package shared32} diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/macosx.yml b/thirdparty/ryml/ext/c4core/.github/workflows/macosx.yml new file mode 100644 index 000000000..33145e5c7 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/macosx.yml @@ -0,0 +1,103 @@ +name: macosx + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +# ubuntu-20.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.2.0 +# clang: 8.0.1, 9.0.1, 10.0.0 +# ubuntu-18.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.1.0 +# clang: 6.0.0, 8.0.0, 9.0.0 +# macos-11.0: macOS Big Sur 11.0 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 10.0.1 +# gcc-8 gcc-9 +# macos-10.15: macOS Catalina 10.15 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 11.0.0 +# gcc-8 gcc-9 +# windows-2019: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md +# vs2019 +# windows-2016: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md +# vs2017 + +jobs: + + #---------------------------------------------------------------------------- + xcode: + name: xcode${{matrix.xcver}}/c++${{matrix.std}}/${{matrix.bt}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {std: 11, cxx: xcode, xcver: 13, bt: Debug , os: macos-11, bitlinks: shared64 static64} + - {std: 11, cxx: xcode, xcver: 13, bt: Release, os: macos-11, bitlinks: shared64 static64} + - {std: 17, cxx: xcode, xcver: 13, bt: Debug , os: macos-11, bitlinks: shared64 static64} + - {std: 17, cxx: xcode, xcver: 13, bt: Release, os: macos-11, bitlinks: shared64 static64} + # + - {std: 11, cxx: xcode, xcver: 12, bt: Debug , os: macos-11, bitlinks: shared64 static64} + - {std: 11, cxx: xcode, xcver: 12, bt: Release, os: macos-11, bitlinks: shared64 static64} + - {std: 17, cxx: xcode, xcver: 12, bt: Debug , os: macos-11, bitlinks: shared64 static64} + - {std: 17, cxx: xcode, xcver: 12, bt: Release, os: macos-11, bitlinks: shared64 static64} + # + - {std: 11, cxx: xcode, xcver: 11, bt: Debug , os: macos-11, bitlinks: shared64 static64} + - {std: 11, cxx: xcode, xcver: 11, bt: Release, os: macos-11, bitlinks: shared64 static64} + - {std: 17, cxx: xcode, xcver: 11, bt: Debug , os: macos-11, bitlinks: shared64 static64} + - {std: 17, cxx: xcode, xcver: 11, bt: Release, os: macos-11, bitlinks: shared64 static64} + env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: xcode, uses: maxim-lobanov/setup-xcode@v1, with: {xcode-version: "${{matrix.xcver}}" }} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} + - {name: shared64-run, run: source .github/setenv.sh && c4_run_test shared64} + - {name: shared64-pack, run: source .github/setenv.sh && c4_package shared64} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_run_test static64} + - {name: static64-pack, run: source .github/setenv.sh && c4_package static64} + - name: shared32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_test shared32} + - {name: shared32-run, run: source .github/setenv.sh && c4_run_test shared32} + - {name: shared32-pack, run: source .github/setenv.sh && c4_package shared32} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} + - {name: static32-pack, run: source .github/setenv.sh && c4_package static32} diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/release.yml b/thirdparty/ryml/ext/c4core/.github/workflows/release.yml new file mode 100644 index 000000000..0f5a13873 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/release.yml @@ -0,0 +1,197 @@ +name: release + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + tags: + - v0.* + - v1.* + - v2.* + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PKG_NAME: c4core- + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +# useful to iterate when fixing the release: +# ver=0.2.1 ; ( set -x ; git tag -d v$ver ; git push origin :v$ver ) ; (set -x ; set -e ; tbump --only-patch --non-interactive $ver ; git add -u ; git commit --amend --no-edit ; git tag --annotate --message "v$ver" "v$ver" ; git push -f --tags origin ) + +jobs: + + gettag: + runs-on: ubuntu-latest + steps: + # use fetch-depth to ensure all tags are fetched + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive, fetch-depth: 0}} + - name: Variables (from tag) + if: contains(github.ref, 'tags/v') + run: | + # https://github.community/t/how-to-get-just-the-tag-name/16241/11 + SRC_TAG=${GITHUB_REF#refs/tags/} + SRC_VERSION=${GITHUB_REF#refs/tags/v} + cat <<EOF > vars.sh + export SRC_TAG=$SRC_TAG + export SRC_VERSION=$SRC_VERSION + EOF + - name: Variables (from commit, no tag) + if: ${{ !contains(github.ref, 'tags/v') }} + run: | + set -x + branch_name=${GITHUB_REF#refs/heads/} + # builds triggered from PRs have the branch_name like this: refs/pull/150/merge + # so filter to eg pr0150_merge + branch_name=`echo $branch_name | sed "s:refs/pull/\([0-9]*\)/\(.*\):pr0\1_\2:"` + # sanitize the branch name; eg merge/foo-bar -> merge_foo_bar + branch_name=`echo $branch_name | sed 's:[/.-]:_:g'` + SRC_TAG=$(git describe || git rev-parse --short HEAD) # eg v0.2.0-110-gda837e0 + SRC_VERSION="${branch_name}-${SRC_TAG}" + cat <<EOF > vars.sh + export SRC_TAG=$SRC_TAG + export SRC_VERSION=$SRC_VERSION + EOF + - name: Verify vars.sh + run: cat vars.sh ; source vars.sh ; echo $SRC_TAG ; echo $SRC_VERSION + - name: Save vars.sh + uses: actions/upload-artifact@v3 + with: {name: vars.sh, path: ./vars.sh} + + #---------------------------------------------------------------------------- + # create source packages + src: + needs: gettag + runs-on: ubuntu-latest + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - name: Download vars.sh + uses: actions/download-artifact@v3 + with: {name: vars.sh, path: ./} + - name: Install python 3.9 + uses: actions/setup-python@v4 + with: { python-version: 3.9 } + - name: Install requirements + run: | + sudo -E pip install git-archive-all + - name: Create source packages + run: | + pwd + ls -lFhp + source vars.sh + echo SRC_TAG=$SRC_TAG + echo SRC_VERSION=$SRC_VERSION + id=${PROJ_PKG_NAME}${SRC_VERSION} + name=${id}-src + mkdir -p assets + git-archive-all --prefix $name assets/$name.tgz + git-archive-all --prefix $name assets/$name.zip + python --version + python tools/amalgamate.py assets/$id.hpp + - name: Save source artifacts + uses: actions/upload-artifact@v3 + with: {name: assets, path: assets} + + #---------------------------------------------------------------------------- + # create c++ packages + cpp: + name: cpp/${{matrix.config.os}}/${{matrix.config.gen}} + needs: gettag + runs-on: ${{matrix.config.os}} + env: {DEV: OFF, BT: Release, OS: "${{matrix.config.os}}", CXX_: "${{matrix.config.cxx}}", GEN: "${{matrix.config.gen}}"} + strategy: + fail-fast: false + matrix: + config: + # name of the artifact | suffix (gen) | suffix (package) | cpack gen | mime type | os | cxx + - {name: Ubuntu 20.04 deb , sfxg: unix64-shared-Release.deb, sfxp: ubuntu-20.04.deb , gen: DEB , mime: vnd.debian.binary-package, os: ubuntu-20.04 } + - {name: Windows VS2019 zip, sfxg: win64-shared-Release.zip , sfxp: windows-vs2019.zip , gen: ZIP , mime: zip , os: windows-2019, cxx: vs2019} + - {name: MacOSX sh , sfxg: apple64-shared-Release.sh, sfxp: macosx-xcode.sh , gen: STGZ , mime: x-sh , os: macos-11.0 , cxx: xcode } + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - name: Download vars.sh + uses: actions/download-artifact@v3 + with: {name: vars.sh, path: ./} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info } + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_target shared64} + - name: shared64-pack + run: source .github/setenv.sh && c4_package shared64 $GEN + - name: shared64-normalize + run: | + set -x + source vars.sh + mkdir -p assets + asset_src=`ls -1 ./build/shared64/${PROJ_PFX_TARGET}*-${{matrix.config.sfxg}}` + asset_dst=./assets/${PROJ_PKG_NAME}${SRC_VERSION}-${{matrix.config.sfxp}} + [ ! -f $asset_src ] && exit 1 + cp -fav $asset_src $asset_dst + - name: Save artifacts + uses: actions/upload-artifact@v3 + with: {name: assets, path: assets} + + #---------------------------------------------------------------------------- + release: + runs-on: ubuntu-latest + needs: + - src + - cpp + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - name: Gather artifacts - ./assets + uses: actions/download-artifact@v3 + with: {name: assets, path: assets} + - name: Verify existing artifacts + run: | + ls -lFhp assets/ + # + # Github + - name: Restore vars.sh + if: contains(github.ref, 'tags/v') + uses: actions/download-artifact@v3 + with: {name: vars.sh, path: ./} + - name: Save vars for following steps + if: contains(github.ref, 'tags/v') + id: vars + run: | + source vars.sh + version_body=${{github.workspace}}/changelog/$SRC_VERSION.md + if [ ! -f $version_body ] ; then + echo "version body file was not found: $version_body" + exit 1 + fi + echo "VERSION=$SRC_VERSION >> $GITHUB_OUTPUT" + echo "VERSION_BODY=$version_body >> $GITHUB_OUTPUT" + - name: Create Github Release + if: contains(github.ref, 'tags/v') + id: create_release + uses: actions/create-release@v1 + env: { GITHUB_TOKEN: "${{secrets.GITHUB_TOKEN}}" } + with: + tag_name: ${{github.ref}} + release_name: Release ${{steps.vars.outputs.VERSION}} + body_path: ${{steps.vars.outputs.VERSION_BODY}} + draft: true + prerelease: ${{contains(github.ref, 'rc')}} + - name: Upload assets to Github Release + if: contains(github.ref, 'tags/v') + uses: dwenegar/upload-release-assets@v1 + env: { GITHUB_TOKEN: "${{secrets.GITHUB_TOKEN}}" } + with: + release_id: ${{steps.create_release.outputs.id}} + assets_path: ./assets/ diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/test_install.yml b/thirdparty/ryml/ext/c4core/.github/workflows/test_install.yml new file mode 100644 index 000000000..7f28e7578 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/test_install.yml @@ -0,0 +1,104 @@ +name: test_install + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + +jobs: + + #---------------------------------------------------------------------------- + install_tests: + name: ${{matrix.name}}/${{matrix.bt}} + # if: github.ref == 'refs/heads/master' + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {name: find_package/linux , sdir: test/test_install , os: ubuntu-20.04, cxx: g++-10 , gen: "-DCMAKE_CXX_COMPILER=g++-10" , tgt: all , bt: Release, vars: "-Dc4core_DIR=$GITHUB_WORKSPACE/$PDIR/lib/cmake/c4core -DC4CORE_TEST_INSTALL_PACKAGE_MODE=ON", commonvars: } + - {name: find_package/linux , sdir: test/test_install , os: ubuntu-20.04, cxx: g++-10 , gen: "-DCMAKE_CXX_COMPILER=g++-10" , tgt: all , bt: Debug , vars: "-Dc4core_DIR=$GITHUB_WORKSPACE/$PDIR/lib/cmake/c4core -DC4CORE_TEST_INSTALL_PACKAGE_MODE=ON", commonvars: } + - {name: find_package/linux/libcxx, sdir: test/test_install , os: ubuntu-20.04, cxx: clang++-9, gen: "-DCMAKE_CXX_COMPILER=clang++-9" , tgt: all , bt: Release, vars: "-Dc4core_DIR=$GITHUB_WORKSPACE/$PDIR/lib/cmake/c4core -DC4CORE_TEST_INSTALL_PACKAGE_MODE=ON", commonvars: "-DC4CORE_USE_LIBCXX=ON"} + - {name: find_package/linux/libcxx, sdir: test/test_install , os: ubuntu-20.04, cxx: clang++-9, gen: "-DCMAKE_CXX_COMPILER=clang++-9" , tgt: all , bt: Debug , vars: "-Dc4core_DIR=$GITHUB_WORKSPACE/$PDIR/lib/cmake/c4core -DC4CORE_TEST_INSTALL_PACKAGE_MODE=ON", commonvars: "-DC4CORE_USE_LIBCXX=ON"} + - {name: find_package/macos , sdir: test/test_install , os: macos-11.0 , cxx: xcode , gen: "-G Xcode -DCMAKE_OSX_ARCHITECTURES=x86_64", tgt: ALL_BUILD, bt: Release, vars: "-Dc4core_DIR=$GITHUB_WORKSPACE/$PDIR/lib/cmake/c4core -DC4CORE_TEST_INSTALL_PACKAGE_MODE=ON", commonvars: } + - {name: find_package/macos , sdir: test/test_install , os: macos-11.0 , cxx: xcode , gen: "-G Xcode -DCMAKE_OSX_ARCHITECTURES=x86_64", tgt: ALL_BUILD, bt: Debug , vars: "-Dc4core_DIR=$GITHUB_WORKSPACE/$PDIR/lib/cmake/c4core -DC4CORE_TEST_INSTALL_PACKAGE_MODE=ON", commonvars: } + - {name: find_package/win , sdir: test/test_install , os: windows-2019, cxx: vs2019 , gen: "-G 'Visual Studio 16 2019' -A x64" , tgt: ALL_BUILD, bt: Release, vars: "-Dc4core_DIR=$GITHUB_WORKSPACE/$PDIR/cmake -DC4CORE_TEST_INSTALL_PACKAGE_MODE=ON", commonvars: } + - {name: find_package/win , sdir: test/test_install , os: windows-2019, cxx: vs2019 , gen: "-G 'Visual Studio 16 2019' -A x64" , tgt: ALL_BUILD, bt: Debug , vars: "-Dc4core_DIR=$GITHUB_WORKSPACE/$PDIR/cmake -DC4CORE_TEST_INSTALL_PACKAGE_MODE=ON", commonvars: } + # + - {name: find_library/linux , sdir: test/test_install , os: ubuntu-20.04, cxx: g++-10 , gen: "-DCMAKE_CXX_COMPILER=g++-10" , tgt: all , bt: Release, vars: "-DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/$PDIR -DC4CORE_TEST_INSTALL_PACKAGE_MODE=OFF", commonvars: } + - {name: find_library/linux , sdir: test/test_install , os: ubuntu-20.04, cxx: g++-10 , gen: "-DCMAKE_CXX_COMPILER=g++-10" , tgt: all , bt: Debug , vars: "-DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/$PDIR -DC4CORE_TEST_INSTALL_PACKAGE_MODE=OFF", commonvars: } + - {name: find_library/linux/libcxx, sdir: test/test_install , os: ubuntu-20.04, cxx: clang++-9, gen: "-DCMAKE_CXX_COMPILER=clang++-9" , tgt: all , bt: Release, vars: "-DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/$PDIR -DC4CORE_TEST_INSTALL_PACKAGE_MODE=OFF", commonvars: "-DC4CORE_USE_LIBCXX=ON"} + - {name: find_library/linux/libcxx, sdir: test/test_install , os: ubuntu-20.04, cxx: clang++-9, gen: "-DCMAKE_CXX_COMPILER=clang++-9" , tgt: all , bt: Debug , vars: "-DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/$PDIR -DC4CORE_TEST_INSTALL_PACKAGE_MODE=OFF", commonvars: "-DC4CORE_USE_LIBCXX=ON"} + - {name: find_library/macos , sdir: test/test_install , os: macos-11.0 , cxx: xcode , gen: "-G Xcode -DCMAKE_OSX_ARCHITECTURES=x86_64", tgt: ALL_BUILD, bt: Release, vars: "-DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/$PDIR -DC4CORE_TEST_INSTALL_PACKAGE_MODE=OFF", commonvars: } + - {name: find_library/macos , sdir: test/test_install , os: macos-11.0 , cxx: xcode , gen: "-G Xcode -DCMAKE_OSX_ARCHITECTURES=x86_64", tgt: ALL_BUILD, bt: Debug , vars: "-DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/$PDIR -DC4CORE_TEST_INSTALL_PACKAGE_MODE=OFF", commonvars: } + - {name: find_library/win , sdir: test/test_install , os: windows-2019, cxx: vs2019 , gen: "-G 'Visual Studio 16 2019' -A x64" , tgt: ALL_BUILD, bt: Release, vars: "-DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/$PDIR -DC4CORE_TEST_INSTALL_PACKAGE_MODE=OFF", commonvars: } + - {name: find_library/win , sdir: test/test_install , os: windows-2019, cxx: vs2019 , gen: "-G 'Visual Studio 16 2019' -A x64" , tgt: ALL_BUILD, bt: Debug , vars: "-DCMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/$PDIR -DC4CORE_TEST_INSTALL_PACKAGE_MODE=OFF", commonvars: } + # + - {name: singleheader/linux , sdir: test/test_singleheader, os: ubuntu-20.04, cxx: g++-10 , gen: "-DCMAKE_CXX_COMPILER=g++-10" , tgt: all , bt: Release, vars: , commonvars: } + - {name: singleheader/linux , sdir: test/test_singleheader, os: ubuntu-20.04, cxx: g++-10 , gen: "-DCMAKE_CXX_COMPILER=g++-10" , tgt: all , bt: Debug , vars: , commonvars: } + - {name: singleheader/linux/libcxx, sdir: test/test_singleheader, os: ubuntu-20.04, cxx: clang++-9, gen: "-DCMAKE_CXX_COMPILER=clang++-9" , tgt: all , bt: Release, vars: , commonvars: "-DC4CORE_USE_LIBCXX=ON"} + - {name: singleheader/linux/libcxx, sdir: test/test_singleheader, os: ubuntu-20.04, cxx: clang++-9, gen: "-DCMAKE_CXX_COMPILER=clang++-9" , tgt: all , bt: Debug , vars: , commonvars: "-DC4CORE_USE_LIBCXX=ON"} + - {name: singleheader/macos , sdir: test/test_singleheader, os: macos-11.0 , cxx: xcode , gen: "-G Xcode -DCMAKE_OSX_ARCHITECTURES=x86_64", tgt: ALL_BUILD, bt: Release, vars: , commonvars: } + - {name: singleheader/macos , sdir: test/test_singleheader, os: macos-11.0 , cxx: xcode , gen: "-G Xcode -DCMAKE_OSX_ARCHITECTURES=x86_64", tgt: ALL_BUILD, bt: Debug , vars: , commonvars: } + - {name: singleheader/win , sdir: test/test_singleheader, os: windows-2019, cxx: vs2019 , gen: "-G 'Visual Studio 16 2019' -A x64" , tgt: ALL_BUILD, bt: Release, vars: , commonvars: } + - {name: singleheader/win , sdir: test/test_singleheader, os: windows-2019, cxx: vs2019 , gen: "-G 'Visual Studio 16 2019' -A x64" , tgt: ALL_BUILD, bt: Debug , vars: , commonvars: } + env: + CXX_: "${{matrix.cxx}}" + BT: "${{matrix.bt}}" + OS: "${{matrix.os}}" + BDIR: "build/${{matrix.name}}-${{matrix.bt}}" + IDIR: "install/${{matrix.name}}-${{matrix.bt}}" + PDIR: "prefix/${{matrix.name}}-${{matrix.bt}}" + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: Install python 3.9 + uses: actions/setup-python@v4 + with: { python-version: 3.9 } + - name: preinstall + run: | + if [ "${{matrix.sdir}}" == "test/test_install" ] ; then + mkdir -p $BDIR-staging + cmake -S . -B $BDIR-staging -DCMAKE_INSTALL_PREFIX=$PDIR -DCMAKE_BUILD_TYPE=${{matrix.bt}} ${{matrix.gen}} ${{matrix.commonvars}} + cmake --build $BDIR-staging --config ${{matrix.bt}} --target ${{matrix.tgt}} -j + cmake --build $BDIR-staging --config ${{matrix.bt}} --target install + fi + - name: configure + run: | + mkdir -p $BDIR + mkdir -p $IDIR + cmake -S ${{matrix.sdir}} -B $BDIR \ + -DC4CORE_BUILD_TESTS=ON \ + -DC4CORE_VALGRIND=OFF \ + -DCMAKE_BUILD_TYPE=${{matrix.bt}} \ + -DCMAKE_INSTALL_PREFIX=$IDIR \ + ${{matrix.gen}} \ + ${{matrix.vars}} \ + ${{matrix.commonvars}} + - name: build + run: | + cmake --build $BDIR --config ${{matrix.bt}} --target c4core-test-build -j + - name: run + run: | + cmake --build $BDIR --config ${{matrix.bt}} --target c4core-test-run diff --git a/thirdparty/ryml/ext/c4core/.github/workflows/windows.yml b/thirdparty/ryml/ext/c4core/.github/workflows/windows.yml new file mode 100644 index 000000000..11a54f185 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.github/workflows/windows.yml @@ -0,0 +1,157 @@ +name: windows + +defaults: + #if: "!contains(github.event.head_commit.message, 'skip ci')" # SKIP + run: + # Use a bash shell so we can use the same syntax for environment variable + # access regardless of the host operating system + shell: bash -e -x {0} + +on: + # https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + +env: + PROJ_PFX_TARGET: c4core- + PROJ_PFX_CMAKE: C4CORE_ + CMAKE_FLAGS: + NUM_JOBS_BUILD: # 4 + + +# ubuntu-20.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu2004-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.2.0 +# clang: 8.0.1, 9.0.1, 10.0.0 +# ubuntu-18.04: +# # https://github.com/actions/virtual-environments/blob/main/images/linux/Ubuntu1804-README.md +# gcc: 7.5.0, 8.4.0, 9.3.0, 10.1.0 +# clang: 6.0.0, 8.0.0, 9.0.0 +# macos-11.0: macOS Big Sur 11.0 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-11.0-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 10.0.1 +# gcc-8 gcc-9 +# macos-10.15: macOS Catalina 10.15 +# # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md +# Xcode 12.1 11.7 +# clang/LLVM 11.0.0 +# gcc-8 gcc-9 +# windows-2019: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md +# vs2019 +# windows-2016: +# # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md +# vs2017 + +jobs: + + #---------------------------------------------------------------------------- + windows: + name: win/${{matrix.cxx}}/c++${{matrix.std}}/${{matrix.bt}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + # github retired windows-2016 + #- {std: 11, cxx: vs2017, bt: Debug , os: windows-2016, bitlinks: shared64 static32} + #- {std: 11, cxx: vs2017, bt: Release, os: windows-2016, bitlinks: shared64 static32} + #- {std: 14, cxx: vs2017, bt: Debug , os: windows-2016, bitlinks: shared64 static32} + #- {std: 14, cxx: vs2017, bt: Release, os: windows-2016, bitlinks: shared64 static32} + # + - {std: 11, cxx: vs2019, bt: Debug , os: windows-2019, bitlinks: shared64 static32} + - {std: 11, cxx: vs2019, bt: Release, os: windows-2019, bitlinks: shared64 static32} + - {std: 14, cxx: vs2019, bt: Debug , os: windows-2019, bitlinks: shared64 static32} + - {std: 14, cxx: vs2019, bt: Release, os: windows-2019, bitlinks: shared64 static32} + - {std: 17, cxx: vs2019, bt: Debug , os: windows-2019, bitlinks: shared64 static32} + - {std: 17, cxx: vs2019, bt: Release, os: windows-2019, bitlinks: shared64 static32} + # + - {std: 11, cxx: vs2022, bt: Debug , os: windows-2022, bitlinks: shared64 static32} + - {std: 11, cxx: vs2022, bt: Release, os: windows-2022, bitlinks: shared64 static32} + - {std: 14, cxx: vs2022, bt: Debug , os: windows-2022, bitlinks: shared64 static32} + - {std: 14, cxx: vs2022, bt: Release, os: windows-2022, bitlinks: shared64 static32} + - {std: 17, cxx: vs2022, bt: Debug , os: windows-2022, bitlinks: shared64 static32} + - {std: 17, cxx: vs2022, bt: Release, os: windows-2022, bitlinks: shared64 static32} + - {std: 20, cxx: vs2022, bt: Debug , os: windows-2022, bitlinks: shared64 static32} + - {std: 20, cxx: vs2022, bt: Release, os: windows-2022, bitlinks: shared64 static32} + env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64 + - {name: shared64-build, run: source .github/setenv.sh && c4_build_test shared64} + - {name: shared64-run, run: source .github/setenv.sh && c4_run_test shared64} + - {name: shared64-pack, run: source .github/setenv.sh && c4_package shared64} + - name: static64-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64 + - {name: static64-build, run: source .github/setenv.sh && c4_build_test static64} + - {name: static64-run, run: source .github/setenv.sh && c4_run_test static64} + - {name: static64-pack, run: source .github/setenv.sh && c4_package static64} + - name: shared32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32 + - {name: shared32-build, run: source .github/setenv.sh && c4_build_test shared32} + - {name: shared32-run, run: source .github/setenv.sh && c4_run_test shared32} + - {name: shared32-pack, run: source .github/setenv.sh && c4_package shared32} + - name: static32-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32 + - {name: static32-build, run: source .github/setenv.sh && c4_build_test static32} + - {name: static32-run, run: source .github/setenv.sh && c4_run_test static32} + - {name: static32-pack, run: source .github/setenv.sh && c4_package static32} + + #---------------------------------------------------------------------------- + # TODO how to run? + windows_arm: + name: win_arm/${{matrix.cxx}}/c++${{matrix.std}}/${{matrix.bt}} + continue-on-error: true + if: always() # https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct + runs-on: ${{matrix.os}} + strategy: + fail-fast: false + matrix: + include: + - {std: 11, cxx: vs2019, bt: Debug , os: windows-2019, bitlinks: shared64arm static32arm} + - {std: 11, cxx: vs2019, bt: Release, os: windows-2019, bitlinks: shared64arm static32arm} + - {std: 17, cxx: vs2019, bt: Debug , os: windows-2019, bitlinks: shared64arm static32arm} + - {std: 17, cxx: vs2019, bt: Release, os: windows-2019, bitlinks: shared64arm static32arm} + # + # vs2022 has an internal compiler error on iarm32 Release builds: + # https://github.com/biojppm/c4core/runs/5593534734?check_suite_focus=true#step:15:126 + - {std: 11, cxx: vs2022, bt: Debug , os: windows-2022, bitlinks: shared64arm static32arm} + - {std: 11, cxx: vs2022, bt: Release, os: windows-2022, bitlinks: shared64arm } + - {std: 20, cxx: vs2022, bt: Debug , os: windows-2022, bitlinks: shared64arm static32arm} + - {std: 20, cxx: vs2022, bt: Release, os: windows-2022, bitlinks: shared64arm } + env: {STD: "${{matrix.std}}", CXX_: "${{matrix.cxx}}", BT: "${{matrix.bt}}", BITLINKS: "${{matrix.bitlinks}}", VG: "${{matrix.vg}}", SAN: "${{matrix.san}}", LINT: "${{matrix.lint}}", OS: "${{matrix.os}}"} + steps: + - {name: checkout, uses: actions/checkout@v3, with: {submodules: recursive}} + - {name: install requirements, run: source .github/reqs.sh && c4_install_test_requirements $OS} + - {name: show info, run: source .github/setenv.sh && c4_show_info} + - name: shared64arm-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared64arm + - {name: shared64arm-build, run: source .github/setenv.sh && c4_build_test shared64arm} + #- {name: shared64arm-run, run: source .github/setenv.sh && c4_run_test shared64arm} + - {name: shared64arm-pack, run: source .github/setenv.sh && c4_package shared64arm} + - name: static64arm-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static64arm + - {name: static64arm-build, run: source .github/setenv.sh && c4_build_test static64arm} + #- {name: static64arm-run, run: source .github/setenv.sh && c4_run_test static64arm} + - {name: static64arm-pack, run: source .github/setenv.sh && c4_package static64arm} + - name: shared32arm-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test shared32arm + - {name: shared32arm-build, run: source .github/setenv.sh && c4_build_test shared32arm} + #- {name: shared32arm-run, run: source .github/setenv.sh && c4_run_test shared32arm} + - {name: shared32arm-pack, run: source .github/setenv.sh && c4_package shared32arm} + - name: static32arm-configure--------------------------------------------------- + run: source .github/setenv.sh && c4_cfg_test static32arm + - {name: static32arm-build, run: source .github/setenv.sh && c4_build_test static32arm} + #- {name: static32arm-run, run: source .github/setenv.sh && c4_run_test static32arm} + - {name: static32arm-pack, run: source .github/setenv.sh && c4_package static32arm} diff --git a/thirdparty/ryml/ext/c4core/.gitignore b/thirdparty/ryml/ext/c4core/.gitignore new file mode 100644 index 000000000..9c258ea3b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.gitignore @@ -0,0 +1,34 @@ +# text editor files +*.bck +\#* +*~ +.ccls-cache/ +.clangd/ +.cache/ +.cquery_cached_index/ +__pycache__/ + +# Visual Studio files +.vs/ +.vscode/ +# QtCreator files +CMakeLists.txt.user +# Eclipse +.project +.cproject +/.settings/ + +# build files +build/ +install/ +.python-version +compile_commands.json + +# test files +/Testing/ + +# continuous integration files +.github/vagrant/*.log +.github/vagrant/.vagrant +.github/vagrant/macos/.vagrant +src_singleheader/
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/.gitmodules b/thirdparty/ryml/ext/c4core/.gitmodules new file mode 100644 index 000000000..77f89eae3 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/.gitmodules @@ -0,0 +1,9 @@ +[submodule "cmake"] + path = cmake + url = https://github.com/biojppm/cmake +[submodule "extern/debugbreak"] + path = src/c4/ext/debugbreak + url = https://github.com/biojppm/debugbreak +[submodule "src/c4/ext/fast_float"] + path = src/c4/ext/fast_float + url = https://github.com/fastfloat/fast_float diff --git a/thirdparty/ryml/ext/c4core/LICENSE-BOOST.txt b/thirdparty/ryml/ext/c4core/LICENSE-BOOST.txt new file mode 100644 index 000000000..689a5fa00 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/LICENSE-BOOST.txt @@ -0,0 +1,26 @@ +src/c4/ext/sg14/inplace_function.h is distributed under the following terms: +---------------------------------------------------------------------------- + +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. diff --git a/thirdparty/ryml/ext/c4core/LICENSE.txt b/thirdparty/ryml/ext/c4core/LICENSE.txt new file mode 100644 index 000000000..47b6b4394 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2018, Joao Paulo Magalhaes <[email protected]> + +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/thirdparty/ryml/ext/c4core/README.md b/thirdparty/ryml/ext/c4core/README.md new file mode 100644 index 000000000..cd420954d --- /dev/null +++ b/thirdparty/ryml/ext/c4core/README.md @@ -0,0 +1,381 @@ +# c4core - C++ core utilities + +[](https://github.com/biojppm/c4core/blob/master/LICENSE.txt) +[](https://c4core.docsforge.com/) +[](https://github.com/biojppm/c4core/actions?query=ci) +<!-- [](https://coveralls.io/github/biojppm/c4core) --> +[](https://codecov.io/gh/biojppm/c4core) +[](https://lgtm.com/projects/g/biojppm/c4core/alerts/) +[](https://lgtm.com/projects/g/biojppm/c4core/context:cpp) + + +c4core is a library of low-level C++ utilities, written with low-latency +projects in mind. + +Some of the utilities provided by c4core have already equivalent +functionality in the C++ standard, but they are provided as the existing C++ +equivalent may be insufficient (eg, std::string_view), inefficient (eg, +std::string), heavy (eg streams), or plainly unusable on some +platforms/projects, (eg exceptions); some other utilities have equivalent +under consideration for C++ standardisation; and yet some other utilities +have (to my knowledge) no equivalent under consideration. Be that as it may, +I've been using these utilities in this or similar forms for some years now, +and I've found them incredibly useful in my projects. I'm packing these as a +separate library, as all of my projects use it. + +c4core is [extensively unit-tested in Linux, Windows and +MacOS](https://github.com/biojppm/c4core/actions). The tests cover +x64, x86, arm, wasm (emscripten), aarch64, ppc64le and s390x +architectures, and include analysing c4core with: + * valgrind + * clang-tidy + * clang sanitizers: + * memory + * address + * undefined behavior + * thread + * [LGTM.com](https://lgtm.com/projects/g/biojppm/c4core) + +c4core also works [in +bare-metal](https://github.com/biojppm/c4core/issues/63) as well as +[in RISC-V](https://github.com/biojppm/c4core/pull/69) but at the +moment it's not easy to add automated tests to the CI, so for now +these are not in the list of official architectures. + +<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc --> +- [c4core - C++ core utilities](#c4core---c-core-utilities) + - [Obtaining c4core](#obtaining-c4core) + - [Using c4core in your project](#using-c4core-in-your-project) + - [CMake](#cmake) + - [Bazel](#bazel) + - [Header-only](#header-only) + - [Package managers](#package-managers) + - [Quick tour](#quick-tour) + - [Writeable string views: c4::substr and c4::csubstr](#writeable-string-views-c4substr-and-c4csubstr) + - [Value <-> character interoperation](#value---character-interoperation) + - [String formatting and parsing](#string-formatting-and-parsing) + - [`c4::span` and `c4::blob`](#c4span-and-c4blob) + - [Enums and enum symbols](#enums-and-enum-symbols) + - [Bitmasks and bitmask symbols](#bitmasks-and-bitmask-symbols) + - [Base64 encoding / decoding](#base64-encoding--decoding) + - [Fuzzy float comparison](#fuzzy-float-comparison) + - [Multi-platform / multi-compiler utilities](#multi-platform--multi-compiler-utilities) + - [Runtime assertions and error handling](#runtime-assertions-and-error-handling) + - [Memory allocation](#memory-allocation) + - [Mass initialization/construction/destruction](#mass-initializationconstructiondestruction) + +<!-- markdown-toc end --> + + +## Obtaining c4core + +c4core uses git submodules. It is best to clone c4core with the `--recursive` +option: + +```bash +# using --recursive makes sure git submodules are also cloned at the same time +git clone --recursive https://github.com/biojppm/c4core +``` + +If you ommit the `--recursive` option, then after cloning you will have to +make git checkout the current version of the submodules, using `git submodule +init` followed by `git submodule update`. + + +## Using c4core in your project + +c4core can be built with [cmake](#cmake), or can be used header only. It can also be obtained through some package managers. + +### CMake + +The recommended way to use c4core is by making it part of your project +by using `add_subdirectory(${path_to_c4core_root})` in your +CMakeLists.txt. Doing this is not intrusive to your cmake project +because c4core is fast to build, also prefixes every cmake +variable with `C4CORE_`. But more importantly, this will enable you to +compile c4core with the exact same compile settings used by your +project. + +Here's a very quick complete example of setting up your project to use +c4core as a cmake subproject: +```cmake +project(foo) + +add_subdirectory(c4core) + +add_library(foo foo.cpp) +target_link_libraries(foo PUBLIC c4core) # that's it! +``` +Note above that the call to `target_link_libraries()` is using PUBLIC +linking. This is required to make sure the include directories from `c4core` +are transitively used by clients of `foo`. + + +### Header-only + +If you prefer to pick a single header to get you quickly going, [there is an amalgamation tool](tools/amalgamate.py) which generates this header: +```console +[user@host c4core]$ python tools/amalgamate.py -h +usage: amalgamate.py [-h] [--fastfloat | --no-fastfloat] [--stl | --no-stl] [output] + +positional arguments: + output output file. defaults to stdout + +options: + -h, --help show this help message and exit + --fastfloat enable fastfloat library. this is the default. + --no-fastfloat enable fastfloat library. the default is --fastfloat. + --stl enable stl interop. this is the default. + --no-stl enable stl interop. the default is --stl. +``` + + +### Package managers + +c4core is available through the following package managers: + + * [vcpkg](https://vcpkg.io/en/packages.html): `vcpkg install c4core` + * Arch Linux/Manjaro: + * [rapidyaml](https://aur.archlinux.org/packages/rapidyaml/) + + + +<!-----------------------------------------------------> + +## Quick tour + +All of the utilities in this library are under the namespace `c4`; any +exposed macros use the prefix `C4_`: eg `C4_ASSERT()`. + + +### Writeable string views: c4::substr and c4::csubstr + +Here: [`#include <c4/substr.hpp>`](src/c4/substr.hpp) + + +### Value <-> character interoperation + +Here: [`#include <c4/charconv.hpp>`](src/c4/charconv.hpp) + +```c++ +// TODO: elaborate on the topics: + +c4::digits_dec(), c4::read_dec(), c4::write_dec() +c4::digits_hex(), c4::read_hex(), c4::write_hex() +c4::digits_oct(), c4::read_oct(), c4::write_oct() +c4::digits_bin(), c4::read_bin(), c4::write_bin() + +c4::utoa(), c4::atou() +c4::itoa(), c4::atoi() +c4::ftoa(), c4::atof() +c4::dtoa(), c4::atod() +c4::xtoa(), c4::atox() + +c4::to_chars(), c4::from_chars() +c4::to_chars_sub() +c4::to_chars_first() +``` + +The charconv funcions above are very fast; even faster than C++'s fastest facility `std::from_chars()`, `std::to_chars()`. For continuous benchmark results, browse through c4core's [github CI benchmark runs](https://github.com/biojppm/c4core/actions/workflows/benchmarks.yml). For example, a benchmark run on Linux/g++11.2 shows that: +- `c4::to_chars()` can be expected to be roughly... + - ~40% to 2x faster than `std::to_chars()` + - ~10x-30x faster than `sprintf()` + - ~50x-100x faster than a naive `stringstream::operator<<()` followed by `stringstream::str()` +- `c4::from_chars()` can be expected to be roughly... + - ~10%-30% faster than `std::from_chars()` + - ~10x faster than `scanf()` + - ~30x-50x faster than a naive `stringstream::str()` followed by `stringstream::operator>>()` + +Here are the results: + +| Write throughput | | Read throughput | | +|:-------------------------|--------:|:-------------------------|---------:| +| **write `uint8_t`** | **MB/s**| **read `uint8_t`** | **MB/s**| +| `c4::to_chars<u8>` | 526.86 | `c4::from_chars<u8>` | 163.06 | +| `std::to_chars<u8>` | 379.03 | `std::from_chars<u8>` | 154.85 | +| `std::sprintf<u8>` | 20.49 | `std::scanf<u8>` | 15.75 | +| `std::stringstream<u8>` | 3.82 | `std::stringstream<u8>` | 3.83 | +| **write `int8_t`** | **MB/s**| **read `int8_t`** | **MB/s**| +| `c4::to_chars<i8>` | 599.98 | `c4::from_chars<i8>` | 184.20 | +| `std::to_chars<i8>` | 246.32 | `std::from_chars<i8>` | 156.40 | +| `std::sprintf<i8>` | 19.15 | `std::scanf<i8>` | 16.44 | +| `std::stringstream<i8>` | 3.83 | `std::stringstream<i8>` | 3.89 | +| **write `uint16_t`** | **MB/s**| **read `uint16_t`** | **MB/s**| +| `c4::to_chars<u16>` | 486.40 | `c4::from_chars<u16>` | 349.48 | +| `std::to_chars<u16>` | 454.24 | `std::from_chars<u16>` | 319.13 | +| `std::sprintf<u16>` | 38.74 | `std::scanf<u16>` | 28.12 | +| `std::stringstream<u16>` | 7.08 | `std::stringstream<u16>`| 6.73 | +| **write `int16_t`** | **MB/s**| **read `int16_t`** | **MB/s**| +| `c4::to_chars<i16>` | 507.44 | `c4::from_chars<i16>` | 282.95 | +| `std::to_chars<i16>` | 297.49 | `std::from_chars<i16>` | 186.18 | +| `std::sprintf<i16>` | 39.03 | `std::scanf<i16>` | 28.45 | +| `std::stringstream<i16>` | 6.98 | `std::stringstream<i16>`| 6.49 | +| **write `uint32_t`** | **MB/s**| **read `uint32_t`** | **MB/s**| +| `c4::to_chars<u32>` | 730.12 | `c4::from_chars<u32>` | 463.95 | +| `std::to_chars<u32>` | 514.76 | `std::from_chars<u32>` | 329.42 | +| `std::sprintf<u32>` | 71.19 | `std::scanf<u32>` | 44.97 | +| `std::stringstream<u32>` | 14.05 | `std::stringstream<u32>`| 12.57 | +| **write `int32_t`** | **MB/s**| **read `int32_t`** | **MB/s**| +| `c4::to_chars<i32>` | 618.76 | `c4::from_chars<i32>` | 345.53 | +| `std::to_chars<i32>` | 394.72 | `std::from_chars<i32>` | 224.46 | +| `std::sprintf<i32>` | 71.14 | `std::scanf<i32>` | 43.49 | +| `std::stringstream<i32>` | 13.91 | `std::stringstream<i32>`| 12.03 | +| **write `uint64_t`** | **MB/s**| **read `uint64_t`** | **MB/s**| +| `c4::to_chars<u64>` | 1118.87 | `c4::from_chars<u64>` | 928.49 | +| `std::to_chars<u64>` | 886.58 | `std::from_chars<u64>` | 759.03 | +| `std::sprintf<u64>` | 140.96 | `std::scanf<u64>` | 91.60 | +| `std::stringstream<u64>` | 28.01 | `std::stringstream<u64>`| 25.00 | +| **write `int64_t`** | **MB/s**| **read `int64_t`** | **MB/s**| +| `c4::to_chars<i64>` | 1198.78 | `c4::from_chars<i64>` | 713.76 | +| `std::to_chars<i64>` | 882.17 | `std::from_chars<i64>` | 646.18 | +| `std::sprintf<i64>` | 138.79 | `std::scanf<i64>` | 90.07 | +| `std::stringstream<i64>` | 27.62 | `std::stringstream<i64>`| 25.12 | + + +Or here are plots for g++12.1 and VS2019 (from the same computer): + +| Linux gxx12.1 | Windows VS2019 | +|---------------|----------------| +|  |  | +|  |  | +|  |  | +|  |  | +|  |  | +|  |  | +| | | +|  |  | +|  |  | +|  |  | +|  |  | + + + +### String formatting and parsing + +* [`#include <c4/format.hpp>`](src/c4/format.hpp) + +```c++ +// TODO: elaborate on the topics: + +c4::cat(), c4::uncat() +c4::catsep(), c4::uncatsep() +c4::format(), c4::unformat() + +c4::catrs() +c4::catseprs() +c4::formatrs() + +// formatting: +c4::fmt::overflow_checked +c4::fmt::real +c4::fmt::boolalpha +c4::fmt::dec +c4::fmt::hex +c4::fmt::oct +c4::fmt::bin +c4::fmt::zpad +c4::fmt::right +c4::fmt::left +c4::fmt::raw, c4::fmt::craw +c4::fmt::base64, c4::fmt::cbase64 +``` + +### `c4::span` and `c4::blob` + +* [`#include <c4/span.hpp>`](src/c4/span.hpp) +* [`#include <c4/blob.hpp>`](src/c4/blob.hpp) + + +### Enums and enum symbols + +[`#include <c4/enum.hpp>`](src/c4/enum.hpp) + +```c++ +// TODO: elaborate on the topics: + +c4::e2str(), c4::str2e() +``` + +### Bitmasks and bitmask symbols + +[`#include <c4/bitmask.hpp>`](src/c4/bitmask.hpp) + +```c++ +// TODO: elaborate on the topics: + +c4::bm2str(), c4::str2bm() +``` + +### Base64 encoding / decoding + +[`#include <c4/base64.hpp>`](src/c4/base64.hpp) + +### Fuzzy float comparison + + +### Multi-platform / multi-compiler utilities + +```c++ +// TODO: elaborate on the topics: +#include <c4/error.hpp> + +C4_RESTRICT, $, c$, $$, c$$ +#include <c4/restrict.hpp> +#include <c4/unrestrict.hpp> + +#include <c4/windows_push.hpp> +#include <c4/windows_pop.hpp> + +C4_UNREACHABLE() + +c4::type_name() + +// portable attributes +C4_LIKELY()/C4_UNLIKELY() +C4_ALWAYS_INLINE +C4_CONST +C4_PURE +C4_HOT +C4_COLD +``` + +### Runtime assertions and error handling + +```c++ +// TODO: elaborate on the topics: + +error callback + +C4_ASSERT() +C4_XASSERT() +C4_CHECK() + +C4_ERROR() +C4_NOT_IMPLEMENTED() +``` + +### Memory allocation + +```c++ +// TODO: elaborate on the topics: + +c4::aalloc(), c4::afree() // aligned allocation + +c4::MemoryResource // global and scope + +c4::Allocator +``` + +### Mass initialization/construction/destruction + +```c++ +// TODO: elaborate on the topics: + +c4::make_room()/c4::destroy_room() +c4::construct()/c4::construct_n() +c4::destroy()/c4::destroy_n() +c4::copy_construct()/c4::copy_construct_n() +c4::copy_assign()/c4::copy_assign_n() +c4::move_construct()/c4::move_construct_n() +c4::move_assign()/c4::move_assign_n() +``` diff --git a/thirdparty/ryml/ext/c4core/ROADMAP.md b/thirdparty/ryml/ext/c4core/ROADMAP.md new file mode 100644 index 000000000..9857514e4 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/ROADMAP.md @@ -0,0 +1,23 @@ +# ROADMAP + +## New features + +These changes will provide new features, and client code can be kept +unchanged. + + +## API changes + +These changes will require client code to be updated. + +* [breaking] drop use of C-style sprintf() formats in error messages and + assertions. Change the implementation to use c4::format() + ```c++ + C4_ASSERT_MSG(sz > s.size(), "sz=%zu s.size()=%zu", sz, s.size()); + // ... the above changes to: + C4_ASSERT_MSG(sz > s.size(), "sz={} s.size()={}", sz, s.size()); + ``` + +## Implementation changes + +* drop calls to sprintf() in charconv.hpp. diff --git a/thirdparty/ryml/ext/c4core/bm/bm.yml b/thirdparty/ryml/ext/c4core/bm/bm.yml new file mode 100644 index 000000000..885dcf7db --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/bm.yml @@ -0,0 +1,32 @@ + +prefix: c4core-bm + +# TODO https://stackoverflow.com/questions/12360547/tool-for-retrieving-the-list-of-functions-and-methods-in-a-c-code-base + +bm: + charconv-atox: + desc: read string to arithmetic value + src: bm_charconv.cpp + template_types: [uint8, int8, uint16, int16, uint32, int32, uint64, int64, float, double] + charconv-xtoa: + desc: write arithmetic value to string + src: bm_charconv.cpp + template_types: [uint8, int8, uint16, int16, uint32, int32, uint64, int64, float, double] + format-cat: + desc: compares stringification of a sequence of heterogeneous general types + src: bm_format.cpp + format-catfile: + desc: compares stringification to a file of a sequence of heterogeneous general types + src: bm_format.cpp + format-catsep: + desc: compares stringification of a sequence of heterogeneous general types + src: bm_format.cpp + format-catsepfile: + desc: compares stringification to a file of a sequence of heterogeneous general types + src: bm_format.cpp + format-format: + desc: compares formatting of a sequence of heterogeneous general types + src: bm_format.cpp + format-formatfile: + desc: compares stringification to a file of a sequence of heterogeneous general types + src: bm_format.cpp diff --git a/thirdparty/ryml/ext/c4core/bm/bm_atox.cpp b/thirdparty/ryml/ext/c4core/bm/bm_atox.cpp new file mode 100644 index 000000000..b4ca2b609 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/bm_atox.cpp @@ -0,0 +1,478 @@ +#include "./bm_charconv.hpp" + + +// this is an exploratory benchmark to compare the possible +// combinations for all the components of the read_dec() algorithm + + +template<class T> +bool range_based_restrictvar0(c4::csubstr s, T * v) +{ + *v = 0; + for(char c : s) + { + if(C4_UNLIKELY(c < '0' || c > '9')) + return false; + *v = (*v) * T(10) + (T(c) - T('0')); + } + return true; +} + +template<class T> +bool range_based_restrictvar1(c4::csubstr s, T *C4_RESTRICT v) +{ + *v = 0; + for(char c : s) + { + if(C4_UNLIKELY(c < '0' || c > '9')) + return false; + *v = (*v) * T(10) + (T(c) - T('0')); + } + return true; +} + +template<class T> +bool indexloop_restrictvar0(c4::csubstr s, T * v) +{ + *v = 0; + for(size_t i = 0; i < s.len; ++i) + { + const char c = s.str[i]; + if(C4_UNLIKELY(c < '0' || c > '9')) + return false; + *v = (*v) * T(10) + (T(c) - T('0')); + } + return true; +} + +template<class T> +bool indexloop_restrictvar1(c4::csubstr s, T *C4_RESTRICT v) +{ + *v = 0; + for(size_t i = 0; i < s.len; ++i) + { + const char c = s.str[i]; + if(C4_UNLIKELY(c < '0' || c > '9')) + return false; + *v = (*v) * T(10) + (T(c) - T('0')); + } + return true; +} + +template<class T> +bool prefer_likely(c4::csubstr s, T * v) +{ + *v = 0; + for(char c : s) + { + if(C4_LIKELY(c >= '0' && c <= '9')) + *v = (*v) * T(10) + (T(c) - T('0')); + else + return false; + } + return true; +} + +template<class T> +bool no_early_return(c4::csubstr s, T *C4_RESTRICT v) +{ + *v = 0; + bool stat = true; + for(char c : s) + { + if(C4_LIKELY(c >= '0' && c <= '9')) + *v = (*v) * T(10) + (T(c) - T('0')); + else + { + stat = false; + break; + } + } + return stat; +} + +template<class T> +bool no_early_return_auto_type(c4::csubstr s, T *C4_RESTRICT v) +{ + *v = 0; + bool stat = true; + for(char c : s) + { + if(C4_LIKELY(c >= '0' && c <= '9')) + *v = (*v) * T(10) + (T)(c - '0'); + else + { + stat = false; + break; + } + } + return stat; +} + +template<class T> +bool no_early_return_auto_type2(c4::csubstr s, T *C4_RESTRICT v) +{ + *v = 0; + bool stat = true; + for(char c : s) + { + if(C4_LIKELY(c >= '0' && c <= '9')) + { + *v *= 10; + *v += (T)(c - '0'); + } + else + { + stat = false; + break; + } + } + return stat; +} + +#define _(i) (T)(s.str[i] - '0') +C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wimplicit-fallthrough") + +template<class T> +C4_ALWAYS_INLINE auto unroll_switch_nocheck(c4::csubstr s, T *C4_RESTRICT v) + -> typename std::enable_if<sizeof(T) == 1, bool>::type +{ + *v = 0; + switch(s.len) + { + case 1: + *v = _(0); + return true; + case 2: + *v = T(10) * _(0) + _(1); + return true; + case 3: + *v = T(100) * _(0) + T(10) * _(1) + _(2); + return true; + } + return false; +} + +template<class T> +C4_ALWAYS_INLINE auto unroll_switch_nocheck(c4::csubstr s, T *C4_RESTRICT v) + -> typename std::enable_if<sizeof(T) == 2, bool>::type +{ + *v = 0; + switch(s.len) + { + case 1: + *v = _(0); + return true; + case 2: + *v = T(10) * _(0) + _(1); + return true; + case 3: + *v = T(100) * _(0) + T(10) * _(1) + _(2); + return true; + case 4: + *v = T(1000) * _(0) + T(100) * _(1) + T(10) * _(2) + _(3); + return true; + case 5: + *v = T(10000) * _(0) + T(1000) * _(1) + T(100) * _(2) + T(10) * _(3) + _(4); + return true; + } + return false; +} + +template<class T> +C4_ALWAYS_INLINE auto unroll_switch_nocheck(c4::csubstr s, T *C4_RESTRICT v) + -> typename std::enable_if<sizeof(T) == 4, bool>::type +{ + switch(s.len) + { + case 1: + *v = _(0); + return true; + case 2: + *v = T(10) * _(0) + _(1); + return true; + case 3: + *v = T(100) * _(0) + T(10) * _(1) + _(2); + return true; + case 4: + *v = T(1000) * _(0) + T(100) * _(1) + T(10) * _(2) + _(3); + return true; + case 5: + *v = T(10000) * _(0) + T(1000) * _(1) + T(100) * _(2) + T(10) * _(3) + _(4); + return true; + case 6: + *v = T(100000) * _(0) + T(10000) * _(1) + T(1000) * _(2) + T(100) * _(3) + T(10) * _(4) + _(5); + return true; + case 7: + *v = T(1000000) * _(0) + T(100000) * _(1) + T(10000) * _(2) + T(1000) * _(3) + T(100) * _(4) + T(10) * _(5) + _(6); + return true; + case 8: + *v = T(10000000) * _(0) + T(1000000) * _(1) + T(100000) * _(2) + T(10000) * _(3) + T(1000) * _(4) + T(100) * _(5) + T(10) * _(6) + _(7); + return true; + case 9: + *v = T(100000000) * _(0) + T(10000000) * _(1) + T(1000000) * _(2) + T(100000) * _(3) + T(10000) * _(4) + T(1000) * _(5) + T(100) * _(6) + T(10) * _(7) + _(8); + return true; + case 10: + *v = T(1000000000) * _(0) + T(100000000) * _(1) + T(10000000) * _(2) + T(1000000) * _(3) + T(100000) * _(4) + T(10000) * _(5) + T(1000) * _(6) + T(100) * _(7) + T(10) * _(8) + _(9); + return true; + } + return false; +} + +template<class T> +C4_ALWAYS_INLINE auto unroll_switch_nocheck(c4::csubstr s, T *C4_RESTRICT v) + -> typename std::enable_if<sizeof(T) == 8, bool>::type +{ + switch(s.len) + { + case 1: + *v = _(0); + return true; + case 2: + *v = T(10) * _(0) + _(1); + return true; + case 3: + *v = T(100) * _(0) + T(10) * _(1) + _(2); + return true; + case 4: + *v = T(1000) * _(0) + T(100) * _(1) + T(10) * _(2) + _(3); + return true; + case 5: + *v = T(10000) * _(0) + T(1000) * _(1) + T(100) * _(2) + T(10) * _(3) + _(4); + return true; + case 6: + *v = T(100000) * _(0) + T(10000) * _(1) + T(1000) * _(2) + T(100) * _(3) + T(10) * _(4) + _(5); + return true; + case 7: + *v = T(1000000) * _(0) + T(100000) * _(1) + T(10000) * _(2) + T(1000) * _(3) + T(100) * _(4) + T(10) * _(5) + _(6); + return true; + case 8: + *v = T(10000000) * _(0) + T(1000000) * _(1) + T(100000) * _(2) + T(10000) * _(3) + T(1000) * _(4) + T(100) * _(5) + T(10) * _(6) + _(7); + return true; + case 9: + *v = T(100000000) * _(0) + T(10000000) * _(1) + T(1000000) * _(2) + T(100000) * _(3) + T(10000) * _(4) + T(1000) * _(5) + T(100) * _(6) + T(10) * _(7) + _(8); + return true; + case 10: + *v = T(1000000000) * _(0) + T(100000000) * _(1) + T(10000000) * _(2) + T(1000000) * _(3) + T(100000) * _(4) + T(10000) * _(5) + T(1000) * _(6) + T(100) * _(7) + T(10) * _(8) + _(9); + return true; + default: + { + size_t i = 0; + *v = 0; + for( ; i + 10 < s.len; ++i) + *v = *v * T(10) + _(i); + *v = T(1000000000) * _(i) + T(100000000) * _(i+1) + T(10000000) * _(i+2) + T(1000000) * _(i+3) + T(100000) * _(i+4) + T(10000) * _(i+5) + T(1000) * _(i+6) + T(100) * _(i+7) + T(10) * _(i+8) + _(i+9); + return true; + } + } + return false; +} + +#define ok(i) (s.str[i] >= '0' && s.str[i] <= '9') + +template<class T> +C4_ALWAYS_INLINE auto unroll_switch(c4::csubstr s, T *C4_RESTRICT v) + -> typename std::enable_if<sizeof(T) == 1, bool>::type +{ + *v = 0; + switch(s.len) + { + case 1: + *v = _(0); + return ok(0); + case 2: + *v = T(10) * _(0) + _(1); + return ok(0) && ok(1); + case 3: + *v = T(100) * _(0) + T(10) * _(1) + _(2); + return ok(0) && ok(1) && ok(2); + } + return false; +} + +template<class T> +C4_ALWAYS_INLINE auto unroll_switch(c4::csubstr s, T *C4_RESTRICT v) + -> typename std::enable_if<sizeof(T) == 2, bool>::type +{ + *v = 0; + switch(s.len) + { + case 1: + *v = _(0); + return true; + case 2: + *v = T(10) * _(0) + _(1); + return true; + case 3: + *v = T(100) * _(0) + T(10) * _(1) + _(2); + return ok(0) && ok(1) && ok(2); + case 4: + *v = T(1000) * _(0) + T(100) * _(1) + T(10) * _(2) + _(3); + return ok(0) && ok(1) && ok(2) && ok(3); + case 5: + *v = T(10000) * _(0) + T(1000) * _(1) + T(100) * _(2) + T(10) * _(3) + _(4); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4); + } + return false; +} + +template<class T> +C4_ALWAYS_INLINE auto unroll_switch(c4::csubstr s, T *C4_RESTRICT v) + -> typename std::enable_if<sizeof(T) == 4, bool>::type +{ + switch(s.len) + { + case 1: + *v = _(0); + return ok(0); + case 2: + *v = T(10) * _(0) + _(1); + return ok(0) && ok(1); + case 3: + *v = T(100) * _(0) + T(10) * _(1) + _(2); + return ok(0) && ok(1) && ok(2); + case 4: + *v = T(1000) * _(0) + T(100) * _(1) + T(10) * _(2) + _(3); + return ok(0) && ok(1) && ok(2) && ok(3); + case 5: + *v = T(10000) * _(0) + T(1000) * _(1) + T(100) * _(2) + T(10) * _(3) + _(4); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4); + case 6: + *v = T(100000) * _(0) + T(10000) * _(1) + T(1000) * _(2) + T(100) * _(3) + T(10) * _(4) + _(5); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5); + case 7: + *v = T(1000000) * _(0) + T(100000) * _(1) + T(10000) * _(2) + T(1000) * _(3) + T(100) * _(4) + T(10) * _(5) + _(6); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5) && ok(6); + case 8: + *v = T(10000000) * _(0) + T(1000000) * _(1) + T(100000) * _(2) + T(10000) * _(3) + T(1000) * _(4) + T(100) * _(5) + T(10) * _(6) + _(7); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5) && ok(6) && ok(7); + case 9: + *v = T(100000000) * _(0) + T(10000000) * _(1) + T(1000000) * _(2) + T(100000) * _(3) + T(10000) * _(4) + T(1000) * _(5) + T(100) * _(6) + T(10) * _(7) + _(8); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5) && ok(6) && ok(7) && ok(8); + case 10: + *v = T(1000000000) * _(0) + T(100000000) * _(1) + T(10000000) * _(2) + T(1000000) * _(3) + T(100000) * _(4) + T(10000) * _(5) + T(1000) * _(6) + T(100) * _(7) + T(10) * _(8) + _(9); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5) && ok(6) && ok(7) && ok(8) && ok(9); + } + return false; +} + +template<class T> +C4_ALWAYS_INLINE auto unroll_switch(c4::csubstr s, T *C4_RESTRICT v) + -> typename std::enable_if<sizeof(T) == 8, bool>::type +{ + switch(s.len) + { + case 1: + *v = _(0); + return ok(0); + case 2: + *v = T(10) * _(0) + _(1); + return ok(0) && ok(1); + case 3: + *v = T(100) * _(0) + T(10) * _(1) + _(2); + return ok(0) && ok(1) && ok(2); + case 4: + *v = T(1000) * _(0) + T(100) * _(1) + T(10) * _(2) + _(3); + return ok(0) && ok(1) && ok(2) && ok(3); + case 5: + *v = T(10000) * _(0) + T(1000) * _(1) + T(100) * _(2) + T(10) * _(3) + _(4); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4); + case 6: + *v = T(100000) * _(0) + T(10000) * _(1) + T(1000) * _(2) + T(100) * _(3) + T(10) * _(4) + _(5); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5); + case 7: + *v = T(1000000) * _(0) + T(100000) * _(1) + T(10000) * _(2) + T(1000) * _(3) + T(100) * _(4) + T(10) * _(5) + _(6); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5) && ok(6); + case 8: + *v = T(10000000) * _(0) + T(1000000) * _(1) + T(100000) * _(2) + T(10000) * _(3) + T(1000) * _(4) + T(100) * _(5) + T(10) * _(6) + _(7); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5) && ok(6) && ok(7); + case 9: + *v = T(100000000) * _(0) + T(10000000) * _(1) + T(1000000) * _(2) + T(100000) * _(3) + T(10000) * _(4) + T(1000) * _(5) + T(100) * _(6) + T(10) * _(7) + _(8); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5) && ok(6) && ok(7) && ok(8); + case 10: + *v = T(1000000000) * _(0) + T(100000000) * _(1) + T(10000000) * _(2) + T(1000000) * _(3) + T(100000) * _(4) + T(10000) * _(5) + T(1000) * _(6) + T(100) * _(7) + T(10) * _(8) + _(9); + return ok(0) && ok(1) && ok(2) && ok(3) && ok(4) && ok(5) && ok(6) && ok(7) && ok(8) && ok(9); + default: + { + size_t i = 0; + *v = 0; + for( ; i + 10 < s.len; ++i) + { + *v = *v * T(10) + _(i); + if(C4_UNLIKELY(!ok(i))) + return false; + } + *v = T(1000000000) * _(i) + T(100000000) * _(i+1) + T(10000000) * _(i+2) + T(1000000) * _(i+3) + T(100000) * _(i+4) + T(10000) * _(i+5) + T(1000) * _(i+6) + T(100) * _(i+7) + T(10) * _(i+8) + _(i+9); + return ok(i) && ok(i+1) && ok(i+2) && ok(i+3) && ok(i+4) && ok(i+5) && ok(i+6) && ok(i+7) && ok(i+8) && ok(i+9); + } + } + return false; +} + +C4_SUPPRESS_WARNING_GCC_CLANG_POP +#undef _ + + + + +#define DECLARE_BM(func) \ +template<class T> \ +void func(bm::State &st) \ +{ \ + random_strings_cref values = mkstrings_positive<T>(); \ + T val = {}; \ + T sum = {}; \ + for(auto _ : st) \ + { \ + C4DOALL(kNumValues) \ + { \ + const bool ok = func(values.next(), &val); \ + sum += (T)(T(ok) + val); \ + } \ + } \ + bm::DoNotOptimize(sum); \ + report<T>(st, kNumValues); \ +} + +#define DEFINE_BM(ty) \ + C4BM_TEMPLATE(unroll_switch_nocheck, ty); \ + C4BM_TEMPLATE(unroll_switch, ty); \ + C4BM_TEMPLATE(indexloop_restrictvar0, ty); \ + C4BM_TEMPLATE(indexloop_restrictvar1, ty); \ + C4BM_TEMPLATE(range_based_restrictvar0, ty); \ + C4BM_TEMPLATE(range_based_restrictvar1, ty); \ + C4BM_TEMPLATE(prefer_likely, ty); \ + C4BM_TEMPLATE(no_early_return, ty); \ + C4BM_TEMPLATE(no_early_return_auto_type, ty); \ + C4BM_TEMPLATE(no_early_return_auto_type2, ty); \ + + +DECLARE_BM(unroll_switch_nocheck) +DECLARE_BM(unroll_switch) +DECLARE_BM(indexloop_restrictvar0) +DECLARE_BM(indexloop_restrictvar1) +DECLARE_BM(range_based_restrictvar0) +DECLARE_BM(range_based_restrictvar1) +DECLARE_BM(prefer_likely) +DECLARE_BM(no_early_return) +DECLARE_BM(no_early_return_auto_type) +DECLARE_BM(no_early_return_auto_type2) + + +DEFINE_BM(uint8_t) +DEFINE_BM(int8_t) +DEFINE_BM(uint16_t) +DEFINE_BM(int16_t) +DEFINE_BM(uint32_t) +DEFINE_BM(int32_t) +DEFINE_BM(uint64_t) +DEFINE_BM(int64_t) + + +int main(int argc, char *argv[]) +{ + //do_test(); + bm::Initialize(&argc, argv); + bm::RunSpecifiedBenchmarks(); + return 0; +} diff --git a/thirdparty/ryml/ext/c4core/bm/bm_charconv.cpp b/thirdparty/ryml/ext/c4core/bm/bm_charconv.cpp new file mode 100644 index 000000000..888fb91b6 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/bm_charconv.cpp @@ -0,0 +1,1617 @@ +#include "./bm_charconv.hpp" + +#include <c4/ext/fast_float.hpp> +#if C4_CPP >= 17 +#include <charconv> +#if defined(__cpp_lib_to_chars) +#define C4CORE_BM_HAVE_TOCHARS 1 +#endif +#include <utility> +#endif + +#ifdef C4CORE_BM_USE_RYU +#include <ryu/ryu.h> +#include <ryu/ryu_parse.h> +#endif + +#ifdef C4CORE_BM_USE_FP +#include <jkj/fp/from_chars/from_chars.h> +#endif + + +// some of the benchmarks do not need to be templates, +// but it helps in the naming scheme. + +// xtoa means <X> to string +// atox means string to <X> + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +C4FOR(T, isint) +c4_digits_dec(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += c4::digits_dec(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +c4_digits_hex(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += c4::digits_hex(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +c4_digits_oct(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += c4::digits_oct(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +c4_digits_bin(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += c4::digits_bin(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +C4FOR(T, isint) +atox_c4_read_dec(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::read_dec(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atoxhex_c4_read_hex(bm::State& st) +{ + random_strings_cref strings = mkstrings_hex_positive<T>(/*with_prefix*/false); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::read_hex(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atoxoct_c4_read_oct(bm::State& st) +{ + random_strings_cref strings = mkstrings_oct_positive<T>(/*with_prefix*/false); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::read_oct(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atoxbin_c4_read_bin(bm::State& st) +{ + random_strings_cref strings = mkstrings_bin_positive<T>(/*with_prefix*/false); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::read_bin(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +C4FOR(T, isint) +xtoa_c4_write_dec(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals_positive<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::write_dec(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +xtoahex_c4_write_hex(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals_positive<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::write_hex(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +xtoaoct_c4_write_oct(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals_positive<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::write_oct(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +xtoabin_c4_write_bin(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals_positive<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::write_bin(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +C4FOR(T, isiint) +xtoa_c4_itoa(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals_positive<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::itoa(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isuint) +xtoa_c4_utoa(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals_positive<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::utoa(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isfloat) +xtoa_c4_ftoa(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::ftoa(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isdouble) +xtoa_c4_dtoa(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::dtoa(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +xtoa_c4_xtoa(bm::State& st) +{ + string_buffer buf; + random_values<T> values = mkvals_positive<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::xtoa(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +xtoa_c4_xtoa(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::xtoa(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- + +C4FOR(T, isiint) +atox_c4_atoi(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::atoi(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isuint) +atox_c4_atou(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::atou(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isfloat) +atox_c4_atof(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::atof(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isdouble) +atox_c4_atod(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::atod(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_c4_atox(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::atox(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_c4_atox(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::atox(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- + +C4FOR(T, isint) +atox_std_atoi(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + val = (T) std::atoi(strings.next_s().c_str()); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_std_atol(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + val = (T) std::atol(strings.next_s().c_str()); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_std_atof(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + val = (T) std::atof(strings.next_s().c_str()); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- + +C4FOR(T, isint) +atox_std_strtol(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::string const& s = strings.next_s(); + val = (T) std::strtol(s.data(), nullptr, 10); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_std_strtoll(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::string const& s = strings.next_s(); + val = (T) std::strtoll(s.data(), nullptr, 10); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_std_strtoul(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::string const& s = strings.next_s(); + val = (T) std::strtoul(s.data(), nullptr, 10); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_std_strtoull(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::string const& s = strings.next_s(); + val = (T) std::strtoull(s.data(), nullptr, 10); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_std_strtof(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::string const& s = strings.next_s(); + val = (T) std::strtof(s.data(), nullptr); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_std_strtod(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::string const& s = strings.next_s(); + val = (T) std::strtod(s.data(), nullptr); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_std_stof(bm::State &st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + val = std::stof(strings.next_s()); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_std_stod(bm::State &st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + val = std::stod(strings.next_s()); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- + +#ifdef C4CORE_BM_USE_RYU +C4FOR(T, isfloat) +atox_ryu_s2f(bm::State &st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::csubstr s = strings.next(); + s2f_n(s.data(), (int) s.size(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isdouble) +atox_ryu_s2d(bm::State &st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::csubstr s = strings.next(); + s2d_n(s.data(), (int) s.size(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isfloat) +xtoa_ryu_f2s(bm::State &st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + f2s_buffered_n(values.next(), buf.buf.str); + } + bm::DoNotOptimize(buf.buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isdouble) +xtoa_ryu_d2s(bm::State &st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + d2s_buffered_n(values.next(), buf.buf.str); + } + bm::DoNotOptimize(buf.buf); + report<T>(st, kNumValues); +} +#endif // C4CORE_BM_USE_RYU + + +//----------------------------------------------------------------------------- + +// fp is still experimental and undocumented; +// some assertions are firing in debug builds +// so we make these benchmarks available only with NDEBUG +#if (defined(C4CORE_BM_USE_FP)) && (!defined(NDEBUG)) +#undef C4CORE_BM_USE_FP +#endif + +#ifdef C4CORE_BM_USE_FP +C4FOR(T, isreal) +atox_fp_from_chars_limited(bm::State &st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::csubstr s = strings.next(); + val = jkj::fp::from_chars_limited<T>(s.begin(), s.end()).to_float(); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} +#endif // C4CORE_BM_USE_FP + +#ifdef C4CORE_BM_USE_FP +C4FOR(T, isreal) +atox_fp_from_chars_unlimited(bm::State &st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::csubstr s = strings.next(); + val = jkj::fp::from_chars_unlimited<T>(s.begin(), s.end()).to_float(); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} +#endif // C4CORE_BM_USE_FP + + +//----------------------------------------------------------------------------- + +C4FOR(T, isreal) +atox_fast_float(bm::State &st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::csubstr s = strings.next(); + fast_float::from_chars(s.begin(), s.end(), val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- + +template<class T> struct fmtspec; +template<> struct fmtspec< uint8_t> { static const char w[], r[]; }; +template<> struct fmtspec< int8_t> { static const char w[], r[]; }; +template<> struct fmtspec<uint16_t> { static const char w[], r[]; }; +template<> struct fmtspec< int16_t> { static const char w[], r[]; }; +template<> struct fmtspec<uint32_t> { static const char w[], r[]; }; +template<> struct fmtspec< int32_t> { static const char w[], r[]; }; +template<> struct fmtspec<uint64_t> { static const char w[], r[]; }; +template<> struct fmtspec< int64_t> { static const char w[], r[]; }; +template<> struct fmtspec< float > { static const char w[], r[]; }; +template<> struct fmtspec< double > { static const char w[], r[]; }; + +constexpr const char fmtspec< uint8_t>::w[] = "%" PRIu8 ; +constexpr const char fmtspec< int8_t>::w[] = "%" PRIi8 ; +constexpr const char fmtspec<uint16_t>::w[] = "%" PRIu16; +constexpr const char fmtspec< int16_t>::w[] = "%" PRIi16; +constexpr const char fmtspec<uint32_t>::w[] = "%" PRIu32; +constexpr const char fmtspec< int32_t>::w[] = "%" PRIi32; +constexpr const char fmtspec<uint64_t>::w[] = "%" PRIu64; +constexpr const char fmtspec< int64_t>::w[] = "%" PRIi64; +constexpr const char fmtspec< float >::w[] = "%g" ; +constexpr const char fmtspec< double >::w[] = "%lg" ; + +constexpr const char fmtspec< uint8_t>::r[] = "%" SCNu8 ; +constexpr const char fmtspec< int8_t>::r[] = "%" SCNi8 ; +constexpr const char fmtspec<uint16_t>::r[] = "%" SCNu16; +constexpr const char fmtspec< int16_t>::r[] = "%" SCNi16; +constexpr const char fmtspec<uint32_t>::r[] = "%" SCNu32; +constexpr const char fmtspec< int32_t>::r[] = "%" SCNi32; +constexpr const char fmtspec<uint64_t>::r[] = "%" SCNu64; +constexpr const char fmtspec< int64_t>::r[] = "%" SCNi64; +constexpr const char fmtspec< float >::r[] = "%g" ; +constexpr const char fmtspec< double >::r[] = "%lg" ; + +C4FOR(T, isint) +xtoa_sprintf(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals_positive<T>(); + for(auto _ : st) + { + C4DOALL(kNumValues) + ::snprintf(buf.buf.str, buf.buf.len, fmtspec<T>::w, values.next()); + } + bm::DoNotOptimize(buf.buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +xtoa_sprintf(bm::State& st) +{ + string_buffer buf; + random_values_cref<T> values = mkvals<T>(); + C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wdouble-promotion") + for(auto _ : st) + { + C4DOALL(kNumValues) + ::snprintf(buf.buf.str, buf.buf.len, fmtspec<T>::w, values.next()); + } + C4_SUPPRESS_WARNING_GCC_CLANG_POP + bm::DoNotOptimize(buf.buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_scanf(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + ::sscanf(strings.next_s().c_str(), fmtspec<T>::r, &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_scanf(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + ::sscanf(strings.next_s().c_str(), fmtspec<T>::r, &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- + +C4FOR(T, isint) +xtoa_sstream(bm::State& st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + std::string out; C4_UNUSED(out); + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::stringstream ss; + ss << values.next(); + out = ss.str(); + } + } + bm::DoNotOptimize(out); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +xtoa_sstream(bm::State& st) +{ + random_values_cref<T> values = mkvals<T>(); + std::string out; C4_UNUSED(out); + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::stringstream ss; + ss << values.next(); + out = ss.str(); + } + } + bm::DoNotOptimize(out); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +xtoa_sstream_reuse(bm::State& st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + std::stringstream ss; + std::string out; C4_UNUSED(out); + for(auto _ : st) + { + C4DOALL(kNumValues) + { + ss.clear(); + ss.str(""); + ss << values.next(); + out = ss.str(); + } + } + bm::DoNotOptimize(out); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +xtoa_sstream_reuse(bm::State& st) +{ + random_values_cref<T> values = mkvals<T>(); + std::stringstream ss; + std::string out; C4_UNUSED(out); + for(auto _ : st) + { + C4DOALL(kNumValues) + { + ss.clear(); + ss.str(""); + ss << values.next(); + out = ss.str(); + } + } + bm::DoNotOptimize(out); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_sstream(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::stringstream ss(strings.next_s()); + ss >> val; + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_sstream(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::stringstream ss(strings.next_s()); + ss >> val; + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_sstream_reuse(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + std::stringstream ss; + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + ss.clear(); + ss.str(strings.next_s()); + ss >> val; + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_sstream_reuse(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + std::stringstream ss; + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + ss.clear(); + ss.str(strings.next_s()); + ss >> val; + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- + +C4FOR(T, isint) +xtoa_std_to_string(bm::State& st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + std::string out; + for(auto _ : st) + { + C4DOALL(kNumValues) + out = std::to_string(values.next()); + } + bm::DoNotOptimize(out); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +xtoa_std_to_string(bm::State& st) +{ + random_values_cref<T> values = mkvals<T>(); + std::string out; + for(auto _ : st) + { + C4DOALL(kNumValues) + out = std::to_string(values.next()); + } + bm::DoNotOptimize(out); + report<T>(st, kNumValues); +} + + +//----------------------------------------------------------------------------- + +C4FOR(T, isint) +xtoa_c4_to_chars(bm::State& st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + string_buffer buf; + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::to_chars(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +xtoa_c4_to_chars(bm::State& st) +{ + random_values_cref<T> values = mkvals<T>(); + string_buffer buf; + for(auto _ : st) + { + C4DOALL(kNumValues) + c4::to_chars(buf, values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_c4_from_chars(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::from_chars(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isint) +atox_c4_from_chars_checked(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::from_chars(strings.next(), c4::fmt::overflow_checked(val)); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +C4FOR(T, isreal) +atox_c4_from_chars(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::from_chars(strings.next(), &val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +#if (C4_CPP >= 17) +C4FOR(T, isint) +xtoa_std_to_chars(bm::State& st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + string_buffer buf; + for(auto _ : st) + { + C4DOALL(kNumValues) + std::to_chars(buf.begin(), buf.end(), values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} +#endif + +#if C4CORE_BM_HAVE_TOCHARS +C4FOR(T, isreal) +xtoa_std_to_chars(bm::State& st) +{ + random_values_cref<T> values = mkvals<T>(); + string_buffer buf; + for(auto _ : st) + { + C4DOALL(kNumValues) + std::to_chars(buf.begin(), buf.end(), values.next()); + } + bm::DoNotOptimize(buf); + report<T>(st, kNumValues); +} +#endif + +#if (C4_CPP >= 17) +C4FOR(T, isint) +atox_std_from_chars(bm::State& st) +{ + random_strings_cref strings = mkstrings_positive<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::csubstr s = strings.next(); + std::from_chars(s.begin(), s.end(), val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} +#endif + +#if C4CORE_BM_HAVE_TOCHARS +C4FOR(T, isreal) +atox_std_from_chars(bm::State& st) +{ + random_strings_cref strings = mkstrings<T>(); + T val = {}, sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + c4::csubstr s = strings.next(); + std::from_chars(s.begin(), s.end(), val); + sum += val; + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} +#endif + + +//----------------------------------------------------------------------------- + +C4BM_TEMPLATE(c4_digits_dec, uint8_t); +C4BM_TEMPLATE(c4_digits_hex, uint8_t); +C4BM_TEMPLATE(c4_digits_oct, uint8_t); +C4BM_TEMPLATE(c4_digits_bin, uint8_t); + +C4BM_TEMPLATE(c4_digits_dec, int8_t); +C4BM_TEMPLATE(c4_digits_hex, int8_t); +C4BM_TEMPLATE(c4_digits_oct, int8_t); +C4BM_TEMPLATE(c4_digits_bin, int8_t); + +C4BM_TEMPLATE(c4_digits_dec, uint16_t); +C4BM_TEMPLATE(c4_digits_hex, uint16_t); +C4BM_TEMPLATE(c4_digits_oct, uint16_t); +C4BM_TEMPLATE(c4_digits_bin, uint16_t); + +C4BM_TEMPLATE(c4_digits_dec, int16_t); +C4BM_TEMPLATE(c4_digits_hex, int16_t); +C4BM_TEMPLATE(c4_digits_oct, int16_t); +C4BM_TEMPLATE(c4_digits_bin, int16_t); + +C4BM_TEMPLATE(c4_digits_dec, uint32_t); +C4BM_TEMPLATE(c4_digits_hex, uint32_t); +C4BM_TEMPLATE(c4_digits_oct, uint32_t); +C4BM_TEMPLATE(c4_digits_bin, uint32_t); + +C4BM_TEMPLATE(c4_digits_dec, int32_t); +C4BM_TEMPLATE(c4_digits_hex, int32_t); +C4BM_TEMPLATE(c4_digits_oct, int32_t); +C4BM_TEMPLATE(c4_digits_bin, int32_t); + +C4BM_TEMPLATE(c4_digits_dec, uint64_t); +C4BM_TEMPLATE(c4_digits_hex, uint64_t); +C4BM_TEMPLATE(c4_digits_oct, uint64_t); +C4BM_TEMPLATE(c4_digits_bin, uint64_t); + +C4BM_TEMPLATE(c4_digits_dec, int64_t); +C4BM_TEMPLATE(c4_digits_hex, int64_t); +C4BM_TEMPLATE(c4_digits_oct, int64_t); +C4BM_TEMPLATE(c4_digits_bin, int64_t); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +C4BM_TEMPLATE(xtoahex_c4_write_hex, uint8_t); +C4BM_TEMPLATE(xtoaoct_c4_write_oct, uint8_t); +C4BM_TEMPLATE(xtoabin_c4_write_bin, uint8_t); + +C4BM_TEMPLATE(xtoahex_c4_write_hex, int8_t); +C4BM_TEMPLATE(xtoaoct_c4_write_oct, int8_t); +C4BM_TEMPLATE(xtoabin_c4_write_bin, int8_t); + +C4BM_TEMPLATE(xtoahex_c4_write_hex, uint16_t); +C4BM_TEMPLATE(xtoaoct_c4_write_oct, uint16_t); +C4BM_TEMPLATE(xtoabin_c4_write_bin, uint16_t); + +C4BM_TEMPLATE(xtoahex_c4_write_hex, int16_t); +C4BM_TEMPLATE(xtoaoct_c4_write_oct, int16_t); +C4BM_TEMPLATE(xtoabin_c4_write_bin, int16_t); + +C4BM_TEMPLATE(xtoahex_c4_write_hex, uint32_t); +C4BM_TEMPLATE(xtoaoct_c4_write_oct, uint32_t); +C4BM_TEMPLATE(xtoabin_c4_write_bin, uint32_t); + +C4BM_TEMPLATE(xtoahex_c4_write_hex, int32_t); +C4BM_TEMPLATE(xtoaoct_c4_write_oct, int32_t); +C4BM_TEMPLATE(xtoabin_c4_write_bin, int32_t); + +C4BM_TEMPLATE(xtoahex_c4_write_hex, uint64_t); +C4BM_TEMPLATE(xtoaoct_c4_write_oct, uint64_t); +C4BM_TEMPLATE(xtoabin_c4_write_bin, uint64_t); + +C4BM_TEMPLATE(xtoahex_c4_write_hex, int64_t); +C4BM_TEMPLATE(xtoaoct_c4_write_oct, int64_t); +C4BM_TEMPLATE(xtoabin_c4_write_bin, int64_t); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +C4BM_TEMPLATE(xtoa_c4_write_dec, uint8_t); +C4BM_TEMPLATE(xtoa_c4_utoa, uint8_t); +C4BM_TEMPLATE(xtoa_c4_xtoa, uint8_t); +C4BM_TEMPLATE(xtoa_c4_to_chars, uint8_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(xtoa_std_to_chars, uint8_t); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, uint8_t); +C4BM_TEMPLATE(xtoa_sprintf, uint8_t); +C4BM_TEMPLATE(xtoa_sstream_reuse, uint8_t); +C4BM_TEMPLATE(xtoa_sstream, uint8_t); + +C4BM_TEMPLATE(xtoa_c4_write_dec, int8_t); +C4BM_TEMPLATE(xtoa_c4_itoa, int8_t); +C4BM_TEMPLATE(xtoa_c4_xtoa, int8_t); +C4BM_TEMPLATE(xtoa_c4_to_chars, int8_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(xtoa_std_to_chars, int8_t); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, int8_t); +C4BM_TEMPLATE(xtoa_sprintf, int8_t); +C4BM_TEMPLATE(xtoa_sstream_reuse, int8_t); +C4BM_TEMPLATE(xtoa_sstream, int8_t); + +C4BM_TEMPLATE(xtoa_c4_write_dec, uint16_t); +C4BM_TEMPLATE(xtoa_c4_utoa, uint16_t); +C4BM_TEMPLATE(xtoa_c4_xtoa, uint16_t); +C4BM_TEMPLATE(xtoa_c4_to_chars, uint16_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(xtoa_std_to_chars, uint16_t); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, uint16_t); +C4BM_TEMPLATE(xtoa_sprintf, uint16_t); +C4BM_TEMPLATE(xtoa_sstream_reuse, uint16_t); +C4BM_TEMPLATE(xtoa_sstream, uint16_t); + +C4BM_TEMPLATE(xtoa_c4_write_dec, int16_t); +C4BM_TEMPLATE(xtoa_c4_itoa, int16_t); +C4BM_TEMPLATE(xtoa_c4_xtoa, int16_t); +C4BM_TEMPLATE(xtoa_c4_to_chars, int16_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(xtoa_std_to_chars, int16_t); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, int16_t); +C4BM_TEMPLATE(xtoa_sprintf, int16_t); +C4BM_TEMPLATE(xtoa_sstream_reuse, int16_t); +C4BM_TEMPLATE(xtoa_sstream, int16_t); + +C4BM_TEMPLATE(xtoa_c4_write_dec, uint32_t); +C4BM_TEMPLATE(xtoa_c4_utoa, uint32_t); +C4BM_TEMPLATE(xtoa_c4_xtoa, uint32_t); +C4BM_TEMPLATE(xtoa_c4_to_chars, uint32_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(xtoa_std_to_chars, uint32_t); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, uint32_t); +C4BM_TEMPLATE(xtoa_sprintf, uint32_t); +C4BM_TEMPLATE(xtoa_sstream_reuse, uint32_t); +C4BM_TEMPLATE(xtoa_sstream, uint32_t); + +C4BM_TEMPLATE(xtoa_c4_write_dec, int32_t); +C4BM_TEMPLATE(xtoa_c4_itoa, int32_t); +C4BM_TEMPLATE(xtoa_c4_xtoa, int32_t); +C4BM_TEMPLATE(xtoa_c4_to_chars, int32_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(xtoa_std_to_chars, int32_t); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, int32_t); +C4BM_TEMPLATE(xtoa_sprintf, int32_t); +C4BM_TEMPLATE(xtoa_sstream_reuse, int32_t); +C4BM_TEMPLATE(xtoa_sstream, int32_t); + +C4BM_TEMPLATE(xtoa_c4_write_dec, uint64_t); +C4BM_TEMPLATE(xtoa_c4_utoa, uint64_t); +C4BM_TEMPLATE(xtoa_c4_xtoa, uint64_t); +C4BM_TEMPLATE(xtoa_c4_to_chars, uint64_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(xtoa_std_to_chars, uint64_t); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, uint64_t); +C4BM_TEMPLATE(xtoa_sprintf, uint64_t); +C4BM_TEMPLATE(xtoa_sstream_reuse, uint64_t); +C4BM_TEMPLATE(xtoa_sstream, uint64_t); + +C4BM_TEMPLATE(xtoa_c4_write_dec, int64_t); +C4BM_TEMPLATE(xtoa_c4_itoa, int64_t); +C4BM_TEMPLATE(xtoa_c4_xtoa, int64_t); +C4BM_TEMPLATE(xtoa_c4_to_chars, int64_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(xtoa_std_to_chars, int64_t); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, int64_t); +C4BM_TEMPLATE(xtoa_sprintf, int64_t); +C4BM_TEMPLATE(xtoa_sstream_reuse, int64_t); +C4BM_TEMPLATE(xtoa_sstream, int64_t); + +C4BM_TEMPLATE(xtoa_c4_ftoa, float); +C4BM_TEMPLATE(xtoa_c4_xtoa, float); +C4BM_TEMPLATE(xtoa_c4_to_chars, float); +#ifdef C4CORE_BM_USE_RYU +C4BM_TEMPLATE(xtoa_ryu_f2s, float); +#endif +#ifdef C4CORE_BM_HAVE_TOCHARS +C4BM_TEMPLATE_TO_CHARS_FLOAT(xtoa_std_to_chars, float); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, float); +C4BM_TEMPLATE(xtoa_sprintf, float); +C4BM_TEMPLATE(xtoa_sstream_reuse, float); +C4BM_TEMPLATE(xtoa_sstream, float); + +C4BM_TEMPLATE(xtoa_c4_dtoa, double); +C4BM_TEMPLATE(xtoa_c4_xtoa, double); +C4BM_TEMPLATE(xtoa_c4_to_chars, double); +#ifdef C4CORE_BM_USE_RYU +C4BM_TEMPLATE(xtoa_ryu_d2s, double); +#endif +#ifdef C4CORE_BM_HAVE_TOCHARS +C4BM_TEMPLATE_TO_CHARS_FLOAT(xtoa_std_to_chars, double); +#endif +C4BM_TEMPLATE(xtoa_std_to_string, double); +C4BM_TEMPLATE(xtoa_sprintf, double); +C4BM_TEMPLATE(xtoa_sstream_reuse, double); +C4BM_TEMPLATE(xtoa_sstream, double); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +C4BM_TEMPLATE(atoxhex_c4_read_hex, uint8_t); +C4BM_TEMPLATE(atoxoct_c4_read_oct, uint8_t); +C4BM_TEMPLATE(atoxbin_c4_read_bin, uint8_t); + +C4BM_TEMPLATE(atoxhex_c4_read_hex, int8_t); +C4BM_TEMPLATE(atoxoct_c4_read_oct, int8_t); +C4BM_TEMPLATE(atoxbin_c4_read_bin, int8_t); + +C4BM_TEMPLATE(atoxhex_c4_read_hex, uint16_t); +C4BM_TEMPLATE(atoxoct_c4_read_oct, uint16_t); +C4BM_TEMPLATE(atoxbin_c4_read_bin, uint16_t); + +C4BM_TEMPLATE(atoxhex_c4_read_hex, int16_t); +C4BM_TEMPLATE(atoxoct_c4_read_oct, int16_t); +C4BM_TEMPLATE(atoxbin_c4_read_bin, int16_t); + +C4BM_TEMPLATE(atoxhex_c4_read_hex, uint32_t); +C4BM_TEMPLATE(atoxoct_c4_read_oct, uint32_t); +C4BM_TEMPLATE(atoxbin_c4_read_bin, uint32_t); + +C4BM_TEMPLATE(atoxhex_c4_read_hex, int32_t); +C4BM_TEMPLATE(atoxoct_c4_read_oct, int32_t); +C4BM_TEMPLATE(atoxbin_c4_read_bin, int32_t); + +C4BM_TEMPLATE(atoxhex_c4_read_hex, uint64_t); +C4BM_TEMPLATE(atoxoct_c4_read_oct, uint64_t); +C4BM_TEMPLATE(atoxbin_c4_read_bin, uint64_t); + +C4BM_TEMPLATE(atoxhex_c4_read_hex, int64_t); +C4BM_TEMPLATE(atoxoct_c4_read_oct, int64_t); +C4BM_TEMPLATE(atoxbin_c4_read_bin, int64_t); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +C4BM_TEMPLATE(atox_c4_read_dec, uint8_t); +C4BM_TEMPLATE(atox_c4_atou, uint8_t); +C4BM_TEMPLATE(atox_c4_atox, uint8_t); +C4BM_TEMPLATE(atox_c4_from_chars, uint8_t); +C4BM_TEMPLATE(atox_c4_from_chars_checked, uint8_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(atox_std_from_chars, uint8_t); +#endif +C4BM_TEMPLATE(atox_std_atoi, uint8_t); +C4BM_TEMPLATE(atox_std_strtoul, uint8_t); +C4BM_TEMPLATE(atox_scanf, uint8_t); +C4BM_TEMPLATE(atox_sstream_reuse, uint8_t); +C4BM_TEMPLATE(atox_sstream, uint8_t); + +C4BM_TEMPLATE(atox_c4_read_dec, int8_t); +C4BM_TEMPLATE(atox_c4_atoi, int8_t); +C4BM_TEMPLATE(atox_c4_atox, int8_t); +C4BM_TEMPLATE(atox_c4_from_chars, int8_t); +C4BM_TEMPLATE(atox_c4_from_chars_checked, int8_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(atox_std_from_chars, int8_t); +#endif +C4BM_TEMPLATE(atox_std_atoi, int8_t); +C4BM_TEMPLATE(atox_std_strtol, int8_t); +C4BM_TEMPLATE(atox_scanf, int8_t); +C4BM_TEMPLATE(atox_sstream_reuse, int8_t); +C4BM_TEMPLATE(atox_sstream, int8_t); + +C4BM_TEMPLATE(atox_c4_read_dec, uint16_t); +C4BM_TEMPLATE(atox_c4_atou, uint16_t); +C4BM_TEMPLATE(atox_c4_atox, uint16_t); +C4BM_TEMPLATE(atox_c4_from_chars, uint16_t); +C4BM_TEMPLATE(atox_c4_from_chars_checked, uint16_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(atox_std_from_chars, uint16_t); +#endif +C4BM_TEMPLATE(atox_std_atoi, uint16_t); +C4BM_TEMPLATE(atox_std_strtoul, uint16_t); +C4BM_TEMPLATE(atox_scanf, uint16_t); +C4BM_TEMPLATE(atox_sstream_reuse, uint16_t); +C4BM_TEMPLATE(atox_sstream, uint16_t); + +C4BM_TEMPLATE(atox_c4_read_dec, int16_t); +C4BM_TEMPLATE(atox_c4_atoi, int16_t); +C4BM_TEMPLATE(atox_c4_atox, int16_t); +C4BM_TEMPLATE(atox_c4_from_chars, int16_t); +C4BM_TEMPLATE(atox_c4_from_chars_checked, int16_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(atox_std_from_chars, int16_t); +#endif +C4BM_TEMPLATE(atox_std_atoi, int16_t); +C4BM_TEMPLATE(atox_std_strtol, int16_t); +C4BM_TEMPLATE(atox_scanf, int16_t); +C4BM_TEMPLATE(atox_sstream_reuse, int16_t); +C4BM_TEMPLATE(atox_sstream, int16_t); + +C4BM_TEMPLATE(atox_c4_read_dec, uint32_t); +C4BM_TEMPLATE(atox_c4_atou, uint32_t); +C4BM_TEMPLATE(atox_c4_atox, uint32_t); +C4BM_TEMPLATE(atox_c4_from_chars, uint32_t); +C4BM_TEMPLATE(atox_c4_from_chars_checked, uint32_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(atox_std_from_chars, uint32_t); +#endif +C4BM_TEMPLATE(atox_std_atoi, uint32_t); +C4BM_TEMPLATE(atox_std_strtoul, uint32_t); +C4BM_TEMPLATE(atox_scanf, uint32_t); +C4BM_TEMPLATE(atox_sstream_reuse, uint32_t); +C4BM_TEMPLATE(atox_sstream, uint32_t); + +C4BM_TEMPLATE(atox_c4_read_dec, int32_t); +C4BM_TEMPLATE(atox_c4_atoi, int32_t); +C4BM_TEMPLATE(atox_c4_atox, int32_t); +C4BM_TEMPLATE(atox_c4_from_chars, int32_t); +C4BM_TEMPLATE(atox_c4_from_chars_checked, int32_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(atox_std_from_chars, int32_t); +#endif +C4BM_TEMPLATE(atox_std_atoi, int32_t); +C4BM_TEMPLATE(atox_std_strtol, int32_t); +C4BM_TEMPLATE(atox_scanf, int32_t); +C4BM_TEMPLATE(atox_sstream_reuse, int32_t); +C4BM_TEMPLATE(atox_sstream, int32_t); + +C4BM_TEMPLATE(atox_c4_read_dec, uint64_t); +C4BM_TEMPLATE(atox_c4_atou, uint64_t); +C4BM_TEMPLATE(atox_c4_atox, uint64_t); +C4BM_TEMPLATE(atox_c4_from_chars, uint64_t); +C4BM_TEMPLATE(atox_c4_from_chars_checked, uint64_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(atox_std_from_chars, uint64_t); +#endif +C4BM_TEMPLATE(atox_std_atol, uint64_t); +C4BM_TEMPLATE(atox_std_strtoull, uint64_t); +C4BM_TEMPLATE(atox_scanf, uint64_t); +C4BM_TEMPLATE(atox_sstream_reuse, uint64_t); +C4BM_TEMPLATE(atox_sstream, uint64_t); + +C4BM_TEMPLATE(atox_c4_read_dec, int64_t); +C4BM_TEMPLATE(atox_c4_atoi, int64_t); +C4BM_TEMPLATE(atox_c4_atox, int64_t); +C4BM_TEMPLATE(atox_c4_from_chars, int64_t); +C4BM_TEMPLATE(atox_c4_from_chars_checked, int64_t); +#if (C4_CPP >= 17) +C4BM_TEMPLATE_TO_CHARS_INT(atox_std_from_chars, int64_t); +#endif +C4BM_TEMPLATE(atox_std_atol, int64_t); +C4BM_TEMPLATE(atox_std_strtoll, int64_t); +C4BM_TEMPLATE(atox_scanf, int64_t); +C4BM_TEMPLATE(atox_sstream_reuse, int64_t); +C4BM_TEMPLATE(atox_sstream, int64_t); + +C4BM_TEMPLATE(atox_c4_atof, float); +C4BM_TEMPLATE(atox_c4_atox, float); +C4BM_TEMPLATE(atox_c4_from_chars, float); +C4BM_TEMPLATE(atox_fast_float, float); +#ifdef C4CORE_BM_HAVE_TOCHARS +C4BM_TEMPLATE_TO_CHARS_FLOAT(atox_std_from_chars, float); +#endif +#ifdef C4CORE_BM_USE_RYU +C4BM_TEMPLATE(atox_ryu_s2f, float); +#endif +#ifdef C4CORE_BM_USE_FP +C4BM_FP_BENCHMARK(atox_fp_from_chars_limited, float); +C4BM_FP_BENCHMARK(atox_fp_from_chars_unlimited, float); +#endif +C4BM_TEMPLATE(atox_std_atof, float); +C4BM_TEMPLATE(atox_std_strtof, float); +C4BM_TEMPLATE(atox_std_stof, float); +C4BM_TEMPLATE(atox_scanf, float); +C4BM_TEMPLATE(atox_sstream_reuse, float); +C4BM_TEMPLATE(atox_sstream, float); + +C4BM_TEMPLATE(atox_c4_atod, double); +C4BM_TEMPLATE(atox_c4_atox, double); +C4BM_TEMPLATE(atox_c4_from_chars, double); +C4BM_TEMPLATE(atox_fast_float, double); +#ifdef C4CORE_BM_HAVE_TOCHARS +C4BM_TEMPLATE_TO_CHARS_FLOAT(atox_std_from_chars, double); +#endif +#ifdef C4CORE_BM_USE_RYU +C4BM_TEMPLATE(atox_ryu_s2d, double); +#endif +#ifdef C4CORE_BM_USE_FP +C4BM_FP_BENCHMARK(atox_fp_from_chars_limited, double); +C4BM_FP_BENCHMARK(atox_fp_from_chars_unlimited, double); +#endif +C4BM_TEMPLATE(atox_std_atof, double); +C4BM_TEMPLATE(atox_std_strtod, double); +C4BM_TEMPLATE(atox_std_stod, double); +C4BM_TEMPLATE(atox_scanf, double); +C4BM_TEMPLATE(atox_sstream_reuse, double); +C4BM_TEMPLATE(atox_sstream, double); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + bm::Initialize(&argc, argv); + bm::RunSpecifiedBenchmarks(); + return 0; +} + +#include <c4/c4_pop.hpp> diff --git a/thirdparty/ryml/ext/c4core/bm/bm_charconv.hpp b/thirdparty/ryml/ext/c4core/bm/bm_charconv.hpp new file mode 100644 index 000000000..3a2b7dc13 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/bm_charconv.hpp @@ -0,0 +1,454 @@ +#include <string> +#include <sstream> +#include <c4/c4_push.hpp> +#include <c4/substr.hpp> +#include <c4/std/std.hpp> +#include <c4/charconv.hpp> +#include <c4/format.hpp> +#include <c4/memory_util.hpp> +#include <inttypes.h> +#include <stdio.h> +#include <algorithm> +#include <stdlib.h> +#include <vector> +#include <c4/ext/rng/rng.hpp> + +inline double getmax(std::vector<double> const& v) +{ + return *(std::max_element(std::begin(v), std::end(v))); +} +inline double getmin(std::vector<double> const& v) +{ + return *(std::min_element(std::begin(v), std::end(v))); +} +inline double getrange(std::vector<double> const& v) +{ + auto min_max = std::minmax_element(std::begin(v), std::end(v)); + return *min_max.second - *min_max.first; +} + +#define _c4bm_stats \ + /*->Repetitions(20)*/ \ + ->DisplayAggregatesOnly(true) \ + ->ComputeStatistics("range", &getrange) \ + ->ComputeStatistics("max", &getmax) \ + ->ComputeStatistics("min", &getmin) + +#define C4BM_TEMPLATE(fn, ...) BENCHMARK_TEMPLATE(fn, __VA_ARGS__) _c4bm_stats + + +// benchmarks depending on c++17 features are disabled using the +// preprocessor. google benchmark has state.SkipWithError() but it +// makes the program return a nonzero exit code when it finishes. So +// we must resort to the preprocessor to conditionally disable these +// benchmarks +#if defined(__cpp_lib_to_chars) || (C4_CPP >= 17) +#define C4BM_TEMPLATE_TO_CHARS_INT(fn, ...) BENCHMARK_TEMPLATE(fn, __VA_ARGS__) _c4bm_stats +#define C4BM_TEMPLATE_TO_CHARS_FLOAT(fn, ...) BENCHMARK_TEMPLATE(fn, __VA_ARGS__) _c4bm_stats +#else +#define C4BM_TEMPLATE_TO_CHARS_INT(fn, ...) void shutup_extra_semicolon() +#define C4BM_TEMPLATE_TO_CHARS_FLOAT(fn, ...) void shutup_extra_semicolon() +#endif + +C4_SUPPRESS_WARNING_GCC_CLANG_PUSH +C4_SUPPRESS_WARNING_GCC_CLANG("-Wdouble-promotion") +C4_SUPPRESS_WARNING_GCC_CLANG("-Wdeprecated") +C4_SUPPRESS_WARNING_GCC_CLANG("-Wsign-conversion") +C4_SUPPRESS_WARNING_GCC_CLANG("-Wconversion") + +#include <benchmark/benchmark.h> + + +namespace bm = benchmark; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// utilities for use in the benchmarks below + + +// facilities to deuglify SFINAE +#define C4FOR(ty, condition) \ + template<class ty> \ + typename std::enable_if<condition(ty), void>::type +#define C4FOR2(ty1, ty2, condition) \ + template<class ty1, class ty2> \ + typename std::enable_if<condition(ty1), void>::type + +#define isint(ty) std::is_integral<ty>::value +#define isiint(ty) std::is_integral<ty>::value && !std::is_unsigned<ty>::value +#define isuint(ty) std::is_integral<ty>::value && std::is_unsigned<ty>::value +#define isreal(ty) std::is_floating_point<ty>::value +#define isfloat(ty) std::is_same<ty, float>::value +#define isdouble(ty) std::is_same<ty, double>::value + + +/** convenience wrapper to avoid boilerplate code */ +template<class T> +void report(bm::State &st, size_t numvals=1) +{ + int64_t iters = (int64_t)(st.iterations() * numvals); + int64_t bytes = (int64_t)(st.iterations() * numvals * sizeof(T)); + st.SetBytesProcessed(bytes); + st.SetItemsProcessed(iters); +} +template<class T> +void report_threadavg(bm::State &st, size_t numvals=1) +{ + int64_t iters = (int64_t)(st.iterations() * numvals); + int64_t bytes = (int64_t)(st.iterations() * numvals * sizeof(T)); + st.SetBytesProcessed(bytes / (int64_t)st.threads()); + st.SetItemsProcessed(iters / (int64_t)st.threads()); +} + +template<class T> +constexpr bool is_pot(T val) +{ + return val > 0 && (val & (val-T(1))) == 0; +} + +constexpr const uint64_t kSeed = 37; +constexpr const size_t kNumValues = 1u<<20; // 1.049M +C4_STATIC_ASSERT(is_pot(kNumValues)); + + +template<class T, class Eng, class Dist> +T gen(Dist &dist, Eng &eng) +{ + return static_cast<T>(dist(eng)); +} + +template<class T, class Eng, class Dist> +T gen_pos(Dist &dist, Eng &eng) +{ + T val = static_cast<T>(dist(eng)); + while(val <= T(0)) + val = static_cast<T>(dist(eng)); + return val; +} + +/** generate in place a random sequence of values: integral version*/ +C4FOR(T, isint) +generate_n(T *begin, T *end) +{ + // do not use T in the distribution: + // N4659 29.6.1.1 [rand.req.genl]/1e requires one of short, int, long, long long, unsigned short, unsigned int, unsigned long, or unsigned long long + std::uniform_int_distribution<int64_t> idist; + c4::rng::pcg rng(kSeed); + for(; begin != end; ++begin) + *begin = gen<T>(idist, rng); +} + +C4FOR(T, isint) +generate_n_positive(T *begin, T *end) +{ + // do not use T in the distribution: + // N4659 29.6.1.1 [rand.req.genl]/1e requires one of short, int, long, long long, unsigned short, unsigned int, unsigned long, or unsigned long long + std::uniform_int_distribution<uint32_t> idist; + c4::rng::pcg rng(kSeed); + for(; begin != end; ++begin) + *begin = gen_pos<T>(idist, rng); +} + +/** generate in place a random sequence of values: real-number version*/ +C4FOR(T, isreal) +generate_n(T *begin, T *end) +{ + c4::rng::pcg rng(kSeed); + // make sure we also have some integral numbers in the real sequence + T *rstart = begin + (std::distance(begin, end) / 20); // 5% integral numbers + std::uniform_int_distribution<uint32_t> idist; + std::uniform_real_distribution<T> rdist; + for(; begin != rstart; ++begin) + *begin = gen<T>(idist, rng); + for(; begin != end; ++begin) + *begin = gen<T>(rdist, rng); +} + +/** generate in place a random sequence of values: real-number version*/ +C4FOR(T, isreal) +generate_n_positive(T *begin, T *end) +{ + c4::rng::pcg rng(kSeed); + // make sure we also have some integral numbers in the real sequence + T *rstart = begin + (std::distance(begin, end) / 20); // 5% integral numbers + std::uniform_int_distribution<int32_t> idist; + std::uniform_real_distribution<T> rdist; + for(; begin != rstart; ++begin) + *begin = gen_pos<T>(idist, rng); + for(; begin != end; ++begin) + *begin = gen_pos<T>(rdist, rng); +} + + +/** a ring buffer with input values for xtoa benchmarks */ +template<class T> +struct random_values +{ + std::vector<T> v; + mutable size_t curr; + size_t szm1; + T next() const { T f = v[curr]; curr = (curr + 1) & szm1; return f; } + random_values(bool positive_only, size_t sz) : v(sz), curr(0), szm1(sz) + { + C4_CHECK(is_pot(sz)); + if(positive_only) + generate_n_positive<T>(&v.front(), &v.back()); + else + generate_n<T>(&v.front(), &v.back()); + } +}; +template<class T> +using random_values_cref = random_values<T> const&; + +template<class T> +random_values_cref<T> mkvals() +{ + static random_values<T> vals(/*positive_only*/false, kNumValues); + return vals; +} +template<class T> +random_values_cref<T> mkvals_positive() +{ + static random_values<T> vals(/*positive_only*/true, kNumValues); + return vals; +} + + +/** a ring buffer with input strings for atox benchmarks */ +struct random_strings +{ + std::vector<std::string> v_s; + std::vector<c4::csubstr> v; + std::vector<char> arena; + mutable size_t curr; + size_t szm1; + + C4_HOT C4_ALWAYS_INLINE c4::csubstr next() const noexcept { c4::csubstr f = v[curr]; curr = (curr + 1) & szm1; return f; } + C4_HOT C4_ALWAYS_INLINE std::string const& next_s() const noexcept { std::string const& f = v_s[curr]; curr = (curr + 1) & szm1; return f; } + + random_strings() = default; + + template<class T> + void _init(random_values<T> const& tmp) + { + C4_CHECK(is_pot(tmp.v.size())); + v.resize(tmp.v.size()); + v_s.resize(tmp.v.size()); + curr = 0; + szm1 = tmp.v.size() - 1; + } + void _build_arena() + { + size_t sum = 0; + for(std::string const& s : v_s) + sum += s.size(); + sum += v_s.size(); + v.resize(v_s.size()); + arena.resize(sum); + size_t pos = 0; + size_t i = 0; + for(std::string const& s : v_s) + { + memcpy(&arena[pos], s.data(), s.size()); + v[i++] = c4::csubstr(&arena[pos], s.size()); + pos += s.size(); + arena[pos++] = '\0'; + } + } + + template<class T> + void init_as(random_values<T> const& tmp) + { + _init(tmp); + for(size_t i = 0; i < v.size(); ++i) + c4::catrs(&v_s[i], tmp.v[i]); + _build_arena(); + } + template<class T> + void init_as_hex(random_values<T> const& tmp, bool with_prefix) + { + _init(tmp); + for(size_t i = 0; i < v.size(); ++i) + { + c4::catrs(&v_s[i], c4::fmt::hex(tmp.v[i])); + if(!with_prefix) + _erase_radix_prefix(&v_s[i]); + } + _build_arena(); + } + template<class T> + void init_as_oct(random_values<T> const& tmp, bool with_prefix) + { + _init(tmp); + for(size_t i = 0; i < v.size(); ++i) + { + c4::catrs(&v_s[i], c4::fmt::oct(tmp.v[i])); + if(!with_prefix) + _erase_radix_prefix(&v_s[i]); + } + _build_arena(); + } + template<class T> + void init_as_bin(random_values<T> const& tmp, bool with_prefix) + { + _init(tmp); + for(size_t i = 0; i < v.size(); ++i) + { + c4::catrs(&v_s[i], c4::fmt::bin(tmp.v[i])); + if(!with_prefix) + _erase_radix_prefix(&v_s[i]); + } + _build_arena(); + } + + static void _erase_radix_prefix(std::string *s) + { + C4_ASSERT(s->front() != '-'); + s->erase(0, 2); + } +}; +using random_strings_cref = random_strings const&; + +template<class T> +random_strings_cref mkstrings() +{ + static random_strings rs; + if(rs.v.empty()) + rs.init_as<T>(mkvals<T>()); + return rs; +} +template<class T> +random_strings_cref mkstrings_positive() +{ + static random_strings rs; + if(rs.v.empty()) + rs.init_as<T>(mkvals_positive<T>()); + return rs; +} +template<class T> +random_strings_cref mkstrings_hex(bool with_prefix=true) +{ + static random_strings rs; + static random_strings rs_wo_prefix; + if(with_prefix) + { + if(rs.v.empty()) + rs.init_as_hex<T>(mkvals<T>()); + return rs; + } + else + { + if(rs_wo_prefix.v.empty()) + rs_wo_prefix.init_as_hex<T>(mkvals<T>(), false); + return rs_wo_prefix; + } +} +template<class T> +random_strings_cref mkstrings_hex_positive(bool with_prefix=true) +{ + static random_strings rs; + static random_strings rs_wo_prefix; + if(with_prefix) + { + if(rs.v.empty()) + rs.init_as_hex<T>(mkvals_positive<T>(), true); + return rs; + } + else + { + if(rs_wo_prefix.v.empty()) + rs_wo_prefix.init_as_hex<T>(mkvals_positive<T>(), false); + return rs_wo_prefix; + } +} +template<class T> +random_strings_cref mkstrings_oct(bool with_prefix=true) +{ + static random_strings rs; + static random_strings rs_wo_prefix; + if(with_prefix) + { + if(rs.v.empty()) + rs.init_as_oct<T>(mkvals<T>(), true); + return rs; + } + else + { + if(rs_wo_prefix.v.empty()) + rs_wo_prefix.init_as_oct<T>(mkvals<T>(), false); + return rs_wo_prefix; + } +} +template<class T> +random_strings_cref mkstrings_oct_positive(bool with_prefix=true) +{ + static random_strings rs; + static random_strings rs_wo_prefix; + if(with_prefix) + { + if(rs.v.empty()) + rs.init_as_oct<T>(mkvals_positive<T>(), true); + return rs; + } + else + { + if(rs_wo_prefix.v.empty()) + rs_wo_prefix.init_as_oct<T>(mkvals_positive<T>(), false); + return rs_wo_prefix; + } +} +template<class T> +random_strings_cref mkstrings_bin(bool with_prefix=true) +{ + static random_strings rs; + static random_strings rs_wo_prefix; + if(with_prefix) + { + if(rs.v.empty()) + rs.init_as_bin<T>(mkvals<T>(), true); + return rs; + } + else + { + if(rs_wo_prefix.v.empty()) + rs_wo_prefix.init_as_bin<T>(mkvals<T>(), false); + return rs_wo_prefix; + } +} +template<class T> +random_strings_cref mkstrings_bin_positive(bool with_prefix=true) +{ + static random_strings rs; + static random_strings rs_wo_prefix; + if(with_prefix) + { + if(rs.v.empty()) + rs.init_as_bin<T>(mkvals_positive<T>(), true); + return rs; + } + else + { + if(rs_wo_prefix.v.empty()) + rs_wo_prefix.init_as_bin<T>(mkvals_positive<T>(), false); + return rs_wo_prefix; + } +} + + +/** a character buffer, easily convertible to c4::substr */ +template<size_t Dim=128> +struct sbuf +{ + char buf_[Dim]; + c4::substr buf; + sbuf() : buf_(), buf(buf_) {} + C4_HOT C4_ALWAYS_INLINE operator c4::substr& () { return buf; } + char* begin() { return buf.begin(); } + char* end() { return buf.end(); } +}; + +using string_buffer = sbuf<>; + +#define C4DOALL(n) for(size_t elm##__LINE__ = 0; elm##__LINE__ < n; ++elm##__LINE__) diff --git a/thirdparty/ryml/ext/c4core/bm/bm_format.cpp b/thirdparty/ryml/ext/c4core/bm/bm_format.cpp new file mode 100644 index 000000000..64d23cbf3 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/bm_format.cpp @@ -0,0 +1,900 @@ +#include <string> +#include <c4/c4_push.hpp> +#include <c4/std/std.hpp> +#include <c4/format.hpp> +#include <c4/dump.hpp> +#include <sstream> +#include <iostream> +#include <fstream> +#include <cstdlib> +#include <vector> +#include <benchmark/benchmark.h> + +namespace bm = benchmark; + +double getmax(std::vector<double> const& v) +{ + return *(std::max_element(std::begin(v), std::end(v))); +} +double getmin(std::vector<double> const& v) +{ + return *(std::min_element(std::begin(v), std::end(v))); +} +double getrange(std::vector<double> const& v) +{ + auto min_max = std::minmax_element(std::begin(v), std::end(v)); + return *min_max.second - *min_max.first; +} + +#define _c4bm_stats \ + /*->Repetitions(20)*/ \ + ->DisplayAggregatesOnly(true) \ + ->ComputeStatistics("range", &getrange) \ + ->ComputeStatistics("max", &getmax) \ + ->ComputeStatistics("min", &getmin) + +#define C4BM(fn) BENCHMARK(fn) _c4bm_stats + +/** convenience wrapper to avoid boilerplate code */ +void report(bm::State &st, size_t sz) +{ + st.SetBytesProcessed(st.iterations() * static_cast<int64_t>(sz)); + st.SetItemsProcessed(st.iterations()); +} + + +const c4::csubstr sep = " --- "; + + +#define _c4argbundle_fmt "hello here you have some numbers: "\ + "1={}, 2={}, 3={}, 4={}, 5={}, 6={}, 7={}, 8={}, 9={}, size_t(283482349)={}, "\ + "\" \"=\"{}\", \"haha\"=\"{}\", std::string(\"hehe\")=\"{}\", "\ + "str=\"{}\"" + +#define _c4argbundle_fmt_printf "hello here you have some numbers: "\ + "1=%d, 2=%d, 3=%d, 4=%d, 5=%d, 6=%d, 7=%d, 8=%d, 9=%d, size_t(283482349)=%zu, "\ + "\" \"=\"%s\", \"haha\"=\"%s\", std::string(\"hehe\")=\"%s\", "\ + "str=\"%s\"" + +#define _c4argbundle_fmt_printf_sep "hello here you have some numbers: "\ + "1=%d%s2=%d%s3=%d%s4=%d%s5=%d%s6=%d%s7=%d%s8=%d%s9=%d%ssize_t(283482349)=%zu%s"\ + "\" \"=\"%s\"%s\"haha\"=\"%s\"%sstd::string(\"hehe\")=\"%s\"%s"\ + "str=\"%s\"" + +#define _c4argbundle \ + 1, 2, 3, 4, 5, 6, 7, 8, 9, size_t(283482349),\ + " ", "haha", std::string("hehe"),\ + std::string("asdlklkasdlkjasd asdlkjasdlkjasdlkjasdoiasdlkjasldkj") + +#define _c4argbundle_printf \ + 1, 2, 3, 4, 5, 6, 7, 8, 9, size_t(283482349),\ + " ", "haha", std::string("hehe").c_str(),\ + std::string("asdlklkasdlkjasd asdlkjasdlkjasdlkjasdoiasdlkjasldkj").c_str() + +#define _c4argbundle_printf_sep \ + 1, sep.str, 2, sep.str, 3, sep.str, 4, sep.str, 5, sep.str, 6, sep.str, 7, sep.str, 8, sep.str, 9, sep.str, size_t(283482349), sep.str,\ + " ", sep.str, "haha", sep.str, std::string("hehe").c_str(), sep.str,\ + std::string("asdlklkasdlkjasd asdlkjasdlkjasdlkjasdoiasdlkjasldkj").c_str() + +#define _c4argbundle_lshift \ + 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << size_t(283482349)\ + << " " << "haha" << std::string("hehe")\ + << std::string("asdlklkasdlkjasd asdlkjasdlkjasdlkjasdoiasdlkjasldkj") + +#define _c4argbundle_lshift_sep \ + 1 << sep << 2 << sep << 3 << sep << 4 << sep << 5 << sep << 6 << sep << 7 << sep << 8 << sep << 9 << sep << size_t(283482349)\ + << sep << " " << sep << "haha" << sep << std::string("hehe")\ + << sep << std::string("asdlklkasdlkjasd asdlkjasdlkjasdlkjasdoiasdlkjasldkj") + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace dump2str { +std::string c_style_subject; +void c_style(c4::csubstr s) { c_style_subject.append(s.str, s.len); } +struct cpp_style +{ + std::string subject = {}; + void operator() (c4::csubstr s) { subject.append(s.str, s.len); } +}; +struct lambda_style +{ + std::string subject = {}; +}; +} // namespace dump2str + +namespace dump2file { +FILE * c_style_subject; +void c_style(c4::csubstr s) { fwrite(s.str, 1, s.len, c_style_subject); } +struct cpp_style +{ + FILE * subject; + cpp_style() : subject(fopen("asdkjhasdkjhsdfoiefkjn", "wb")) {} + ~cpp_style() { fclose(subject); } + void operator() (c4::csubstr s) { fwrite(s.str, 1, s.len, subject); } +}; +struct lambda_style +{ + lambda_style() : subject(fopen("asdkjhasdkjhsdfoiefkjn", "wb")) {} + ~lambda_style() { fclose(subject); } + FILE * subject; +}; +} // namespace dump2fil + +template<class T> +C4_ALWAYS_INLINE typename std::enable_if<std::is_arithmetic<T>::value, std::string>::type +std_to_string(T const& a) +{ + return std::to_string(a); +} + +template<class T> +C4_ALWAYS_INLINE typename std::enable_if<std::is_same<T, std::string>::value, std::string const&>::type +std_to_string(std::string const& a) +{ + return a; +} + +C4_ALWAYS_INLINE std::string std_to_string(c4::csubstr a) +{ + return std::string(a.str, a.len); +} + +template<class T> +C4_ALWAYS_INLINE typename std::enable_if< ! std::is_arithmetic<T>::value, std::string>::type +std_to_string(T const& a) +{ + return std::string(a); +} + +C4_ALWAYS_INLINE void cat_std_string_impl(std::string *) +{ +} + +C4_ALWAYS_INLINE void catsep_std_string_impl(std::string *) +{ +} + +template<class Arg, class... Args> +void cat_std_string_impl(std::string *s, Arg const& a, Args const& ...args) +{ + *s += std_to_string(a); + cat_std_string_impl(s, args...); +} + +template<class Arg, class... Args> +void catsep_std_string_impl(std::string *s, Arg const& a, Args const& ...args) +{ + *s += std_to_string(a); + if(sizeof...(args) > 0) + { + s->append(sep.str, sep.len); + catsep_std_string_impl(s, args...); + } +} + +void cat_std_stringstream_impl(std::stringstream &) +{ +} +void catsep_std_stringstream_impl(std::stringstream &) +{ +} + +template<class Arg, class... Args> +void cat_std_stringstream_impl(std::stringstream &ss, Arg const& a, Args const& ...args) +{ + ss << a; + cat_std_stringstream_impl(ss, args...); +} + +template<class Arg, class... Args> +void catsep_std_stringstream_impl(std::stringstream &ss, Arg const& a, Args const& ...args) +{ + ss << sep << a; + cat_std_stringstream_impl(ss, args...); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void cat_c4cat_substr(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = 0; + for(auto _ : st) + { + sz = cat(buf, _c4argbundle); + } + report(st, sz); +} + +void cat_c4catrs_reuse(bm::State &st) +{ + std::string buf; + size_t sz = 0; + for(auto _ : st) + { + c4::catrs(&buf, _c4argbundle); + sz = buf.size(); + } + report(st, sz); +} + +void cat_c4catrs_no_reuse(bm::State &st) +{ + size_t sz = 0; + for(auto _ : st) + { + auto buf = c4::catrs<std::string>(_c4argbundle); + sz = buf.size(); + } + report(st, sz); +} + +void cat_c4catdump_c_style_static_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::cat(buf, _c4argbundle); + for(auto _ : st) + { + c4::cat_dump<&dump2str::c_style>(buf, _c4argbundle); + } + report(st, sz); +} + +void cat_c4catdump_c_style_dynamic_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::cat(buf, _c4argbundle); + for(auto _ : st) + { + sz = c4::cat_dump(&dump2str::c_style, buf, _c4argbundle); + } + report(st, sz); +} + +void cat_c4catdump_cpp_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::cat(buf, _c4argbundle); + dump2str::cpp_style dumper; + for(auto _ : st) + { + sz = c4::cat_dump(dumper, buf, _c4argbundle); + } + report(st, sz); +} + +void cat_c4catdump_lambda_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::cat(buf, _c4argbundle); + dump2str::lambda_style dumper; + auto lambda = [&dumper](c4::csubstr s) { dumper.subject.append(s.str, s.len); }; + for(auto _ : st) + { + sz = c4::cat_dump(lambda, buf, _c4argbundle); + } + report(st, sz); +} + +void cat_stdsstream_reuse(bm::State &st) +{ + size_t sz = 0; + std::stringstream ss; + for(auto _ : st) + { + ss.clear(); + ss.str(""); + cat_std_stringstream_impl(ss, _c4argbundle); + sz = ss.str().size(); + } + report(st, sz); +} + +void cat_stdsstream_no_reuse(bm::State &st) +{ + size_t sz = 0; + for(auto _ : st) + { + std::stringstream ss; + cat_std_stringstream_impl(ss, _c4argbundle); + sz = ss.str().size(); + } + report(st, sz); +} + +void cat_std_to_string_reuse(bm::State &st) +{ + size_t sz = 0; + std::string s; + for(auto _ : st) + { + s.clear(); + cat_std_string_impl(&s, _c4argbundle); + sz = s.size(); + } + report(st, sz); +} + +void cat_std_to_string_no_reuse(bm::State &st) +{ + size_t sz = 0; + for(auto _ : st) + { + std::string s; + cat_std_string_impl(&s, _c4argbundle); + sz = s.size(); + } + report(st, sz); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void catfile_c4catdump_c_style_static_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + dump2file::cpp_style fileowner; + dump2file::c_style_subject = fileowner.subject; + size_t sz = c4::cat(buf, _c4argbundle); + for(auto _ : st) + { + c4::cat_dump<&dump2file::c_style>(buf, _c4argbundle); + } + report(st, sz); +} + +void catfile_c4catdump_c_style_dynamic_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + dump2file::cpp_style fileowner; + dump2file::c_style_subject = fileowner.subject; + size_t sz = c4::cat(buf, _c4argbundle); + for(auto _ : st) + { + c4::cat_dump(&dump2file::c_style, buf, _c4argbundle); + } + report(st, sz); +} + +void catfile_c4catdump_cpp_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::cat(buf, _c4argbundle); + dump2file::cpp_style dumper; + for(auto _ : st) + { + sz = c4::cat_dump(dumper, buf, _c4argbundle); + } + report(st, sz); +} + +void catfile_c4catdump_lambda_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::cat(buf, _c4argbundle); + dump2file::lambda_style dumper; + auto lambda = [&dumper](c4::csubstr s) { fwrite(s.str, 1, s.len, dumper.subject); }; + for(auto _ : st) + { + sz = c4::cat_dump(lambda, buf, _c4argbundle); + } + report(st, sz); +} + +void catfile_fprintf(bm::State &st) +{ + char buf[256]; + size_t sz = c4::cat(buf, _c4argbundle); + dump2file::cpp_style dumper; + for(auto _ : st) + { + fprintf(dumper.subject, _c4argbundle_fmt_printf, _c4argbundle_printf); + } + report(st, sz); +} + +void catfile_ofstream(bm::State &st) +{ + char buf[256]; + size_t sz = c4::cat(buf, _c4argbundle); + std::ofstream ofs("ddofgufgbmn4g0rtglf", std::ios::out|std::ios::binary); + for(auto _ : st) + { + ofs << _c4argbundle_lshift; + } + report(st, sz); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void catsep_c4cat_substr(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = 0; + for(auto _ : st) + { + sz = catsep(buf, _c4argbundle); + } + report(st, sz); +} + +void catsep_c4catrs_reuse(bm::State &st) +{ + std::string buf; + size_t sz = 0; + for(auto _ : st) + { + c4::catseprs(&buf, _c4argbundle); + sz = buf.size(); + } + report(st, sz); +} + +void catsep_c4catrs_no_reuse(bm::State &st) +{ + size_t sz = 0; + for(auto _ : st) + { + auto buf = c4::catseprs<std::string>(sep, _c4argbundle); + sz = buf.size(); + } + report(st, sz); +} + +void catsep_c4catdump_c_style_static_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::catsep(buf, _c4argbundle); + for(auto _ : st) + { + c4::catsep_dump<&dump2str::c_style>(buf, sep, _c4argbundle); + } + report(st, sz); +} + +void catsep_c4catdump_c_style_dynamic_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::catsep(buf, _c4argbundle); + for(auto _ : st) + { + sz = c4::catsep_dump(&dump2str::c_style, buf, sep, _c4argbundle); + } + report(st, sz); +} + +void catsep_c4catdump_cpp_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::catsep(buf, _c4argbundle); + dump2str::cpp_style dumper; + for(auto _ : st) + { + sz = c4::catsep_dump(dumper, buf, sep, _c4argbundle); + } + report(st, sz); +} + +void catsep_c4catdump_lambda_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::catsep(buf, sep, _c4argbundle); + dump2str::lambda_style dumper; + auto lambda = [&dumper](c4::csubstr s) { dumper.subject.append(s.str, s.len); }; + for(auto _ : st) + { + sz = c4::catsep_dump(lambda, buf, _c4argbundle); + } + report(st, sz); +} + +void catsep_stdsstream_reuse(bm::State &st) +{ + size_t sz = 0; + std::stringstream ss; + for(auto _ : st) + { + ss.clear(); + ss.str(""); + catsep_std_stringstream_impl(ss, sep, _c4argbundle); + sz = ss.str().size(); + } + report(st, sz); +} + +void catsep_stdsstream_no_reuse(bm::State &st) +{ + size_t sz = 0; + for(auto _ : st) + { + std::stringstream ss; + catsep_std_stringstream_impl(ss, sep, _c4argbundle); + sz = ss.str().size(); + } + report(st, sz); +} + +void catsep_std_to_string_reuse(bm::State &st) +{ + size_t sz = 0; + std::string s; + for(auto _ : st) + { + s.clear(); + catsep_std_string_impl(&s, sep, _c4argbundle); + sz = s.size(); + } + report(st, sz); +} + +void catsep_std_to_string_no_reuse(bm::State &st) +{ + size_t sz = 0; + for(auto _ : st) + { + std::string s; + catsep_std_string_impl(&s, sep, _c4argbundle); + sz = s.size(); + } + report(st, sz); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void catsepfile_c4catsepdump_c_style_static_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + dump2file::cpp_style fileowner; + dump2file::c_style_subject = fileowner.subject; + size_t sz = c4::catsep(buf, sep, _c4argbundle); + for(auto _ : st) + { + c4::catsep_dump<&dump2file::c_style>(buf, sep, _c4argbundle); + } + report(st, sz); +} + +void catsepfile_c4catsepdump_c_style_dynamic_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + dump2file::cpp_style fileowner; + dump2file::c_style_subject = fileowner.subject; + size_t sz = c4::catsep(buf, sep, _c4argbundle); + for(auto _ : st) + { + c4::catsep_dump(&dump2file::c_style, buf, sep, _c4argbundle); + } + report(st, sz); +} + +void catsepfile_c4catsepdump_cpp_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::catsep(buf, sep, _c4argbundle); + dump2file::cpp_style dumper; + for(auto _ : st) + { + c4::catsep_dump(dumper, buf, sep, _c4argbundle); + } + report(st, sz); +} + +void catsepfile_c4catsepdump_lambda_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::catsep(buf, sep, _c4argbundle); + dump2file::lambda_style dumper; + auto lambda = [&dumper](c4::csubstr s) { fwrite(s.str, 1, s.len, dumper.subject); }; + for(auto _ : st) + { + c4::catsep_dump(lambda, buf, sep, _c4argbundle); + } + report(st, sz); +} + +void catsepfile_fprintf(bm::State &st) +{ + char buf[256]; + size_t sz = c4::catsep(buf, sep, _c4argbundle); + dump2file::cpp_style dumper; + for(auto _ : st) + { + fprintf(dumper.subject, _c4argbundle_fmt_printf_sep, _c4argbundle_printf_sep); + } + report(st, sz); +} + +void catsepfile_ofstream(bm::State &st) +{ + char buf[256]; + size_t sz = c4::catsep(buf, sep, _c4argbundle); + std::ofstream ofs("ddofgufgbmn4g0rtglf", std::ios::out|std::ios::binary); + for(auto _ : st) + { + ofs << _c4argbundle_lshift_sep; + } + report(st, sz); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void format_c4format(bm::State &st) +{ + char buf_[512]; + c4::substr buf(buf_); + size_t sz = 0; + for(auto _ : st) + { + sz = format(buf, _c4argbundle_fmt, _c4argbundle); + } + report(st, sz); +} + +void format_c4formatrs_reuse(bm::State &st) +{ + std::string buf; + size_t sz = 0; + for(auto _ : st) + { + c4::formatrs(&buf, _c4argbundle_fmt, _c4argbundle); + sz = buf.size(); + } + report(st, sz); +} + +void format_c4formatrs_no_reuse(bm::State &st) +{ + size_t sz = 0; + for(auto _ : st) + { + auto buf = c4::formatrs<std::string>(_c4argbundle_fmt, _c4argbundle); + sz = buf.size(); + } + report(st, sz); +} + +void format_c4formatdump_c_style_static_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::format(buf, _c4argbundle_fmt, _c4argbundle); + for(auto _ : st) + { + c4::format_dump<&dump2str::c_style>(buf, _c4argbundle_fmt, _c4argbundle); + } + report(st, sz); +} + +void format_c4formatdump_c_style_dynamic_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::format(buf, _c4argbundle_fmt, _c4argbundle); + for(auto _ : st) + { + c4::format_dump(&dump2str::c_style, buf, _c4argbundle_fmt, _c4argbundle); + } + report(st, sz); +} + +void format_c4formatdump_cpp_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::format(buf, _c4argbundle_fmt, _c4argbundle); + dump2str::cpp_style dumper; + for(auto _ : st) + { + c4::format_dump(dumper, buf, _c4argbundle_fmt, _c4argbundle); + } + report(st, sz); +} + +void format_c4formatdump_lambda_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::format(buf, _c4argbundle_fmt, _c4argbundle); + dump2str::lambda_style dumper; + auto lambda = [&dumper](c4::csubstr s) { dumper.subject.append(s.str, s.len); }; + for(auto _ : st) + { + c4::format_dump(lambda, buf, _c4argbundle_fmt, _c4argbundle); + } + report(st, sz); +} + +void format_snprintf(bm::State &st) +{ + char buf_[512]; + c4::substr buf(buf_); + size_t sz = 0; + for(auto _ : st) + { + sz = (size_t) snprintf(buf.str, buf.len, _c4argbundle_fmt_printf, _c4argbundle_printf); + } + report(st, sz); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void formatfile_c4formatdump_c_style_static_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + dump2file::cpp_style fileowner; + dump2file::c_style_subject = fileowner.subject; + size_t sz = c4::format(buf, _c4argbundle_fmt, _c4argbundle); + for(auto _ : st) + { + c4::format_dump<&dump2file::c_style>(buf, _c4argbundle_fmt, _c4argbundle); + } + report(st, sz); +} + +void formatfile_c4formatdump_c_style_dynamic_dispatch(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + dump2file::cpp_style fileowner; + dump2file::c_style_subject = fileowner.subject; + size_t sz = c4::format(buf, _c4argbundle_fmt, _c4argbundle); + for(auto _ : st) + { + c4::format_dump(&dump2file::c_style, buf, _c4argbundle_fmt, _c4argbundle); + } + report(st, sz); +} + +void formatfile_c4formatdump_cpp_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::format(buf, _c4argbundle_fmt, _c4argbundle); + dump2file::cpp_style dumper; + for(auto _ : st) + { + c4::format_dump(dumper, buf, _c4argbundle_fmt, _c4argbundle); + } + report(st, sz); +} + +void formatfile_c4formatdump_lambda_style(bm::State &st) +{ + char buf_[256]; + c4::substr buf(buf_); + size_t sz = c4::format(buf, _c4argbundle_fmt, _c4argbundle); + dump2file::lambda_style dumper; + auto lambda = [&dumper](c4::csubstr s) { fwrite(s.str, 1, s.len, dumper.subject); }; + for(auto _ : st) + { + c4::format_dump(lambda, buf, _c4argbundle_fmt, _c4argbundle); + } + report(st, sz); +} + +void formatfile_fprintf(bm::State &st) +{ + char buf[256]; + size_t sz = c4::format(buf, _c4argbundle_fmt, _c4argbundle); + dump2file::cpp_style dumper; + for(auto _ : st) + { + fprintf(dumper.subject, _c4argbundle_fmt_printf, _c4argbundle_printf); + } + report(st, sz); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +C4BM(cat_c4cat_substr); +C4BM(cat_c4catrs_reuse); +C4BM(cat_c4catrs_no_reuse); +C4BM(cat_c4catdump_c_style_static_dispatch); +C4BM(cat_c4catdump_c_style_dynamic_dispatch); +C4BM(cat_c4catdump_cpp_style); +C4BM(cat_c4catdump_lambda_style); +C4BM(cat_std_to_string_reuse); +C4BM(cat_std_to_string_no_reuse); +C4BM(cat_stdsstream_reuse); +C4BM(cat_stdsstream_no_reuse); + +C4BM(catfile_c4catdump_c_style_static_dispatch); +C4BM(catfile_c4catdump_c_style_dynamic_dispatch); +C4BM(catfile_c4catdump_cpp_style); +C4BM(catfile_c4catdump_lambda_style); +C4BM(catfile_fprintf); +C4BM(catfile_ofstream); + + +C4BM(catsep_c4cat_substr); +C4BM(catsep_c4catrs_reuse); +C4BM(catsep_c4catrs_no_reuse); +C4BM(catsep_c4catdump_c_style_static_dispatch); +C4BM(catsep_c4catdump_c_style_dynamic_dispatch); +C4BM(catsep_c4catdump_cpp_style); +C4BM(catsep_c4catdump_lambda_style); +C4BM(catsep_std_to_string_reuse); +C4BM(catsep_std_to_string_no_reuse); +C4BM(catsep_stdsstream_reuse); +C4BM(catsep_stdsstream_no_reuse); + +C4BM(catsepfile_c4catsepdump_c_style_static_dispatch); +C4BM(catsepfile_c4catsepdump_c_style_dynamic_dispatch); +C4BM(catsepfile_c4catsepdump_cpp_style); +C4BM(catsepfile_c4catsepdump_lambda_style); +C4BM(catsepfile_fprintf); +C4BM(catsepfile_ofstream); + + +C4BM(format_c4format); +C4BM(format_c4formatrs_reuse); +C4BM(format_c4formatrs_no_reuse); +C4BM(format_c4formatdump_c_style_static_dispatch); +C4BM(format_c4formatdump_c_style_dynamic_dispatch); +C4BM(format_c4formatdump_cpp_style); +C4BM(format_c4formatdump_lambda_style); +C4BM(format_snprintf); + +C4BM(formatfile_c4formatdump_c_style_static_dispatch); +C4BM(formatfile_c4formatdump_c_style_dynamic_dispatch); +C4BM(formatfile_c4formatdump_cpp_style); +C4BM(formatfile_c4formatdump_lambda_style); +C4BM(formatfile_fprintf); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + bm::Initialize(&argc, argv); + bm::RunSpecifiedBenchmarks(); + return 0; +} + + +#include <c4/c4_pop.hpp> + + diff --git a/thirdparty/ryml/ext/c4core/bm/bm_itoa_threads.cpp b/thirdparty/ryml/ext/c4core/bm/bm_itoa_threads.cpp new file mode 100644 index 000000000..0e102065a --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/bm_itoa_threads.cpp @@ -0,0 +1,356 @@ +#include "./bm_charconv.hpp" + +#include <chrono> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <thread> +#include <vector> +#include <sstream> +#if defined(__cpp_lib_to_chars) || (C4_CPP >= 17) +#define C4_HAS_STDTOCHARS 1 +#else +#define C4_HAS_STDTOCHARS 0 +#endif +#if C4_HAS_STDTOCHARS +#include <charconv> +#endif + +C4_SUPPRESS_WARNING_GCC_CLANG_PUSH +C4_SUPPRESS_WARNING_GCC_CLANG("-Wcast-align") +C4_SUPPRESS_WARNING_GCC("-Wuseless-cast") +#define FMT_HEADER_ONLY +#include <fmt/format.h> + +#define STB_SPRINTF_IMPLEMENTATION +#include <stb_sprintf.h> +C4_SUPPRESS_WARNING_GCC_POP + +#if C4_CXX >= 20 +#include <version> +#define C4_HAS_STD_FORMAT (__has_cpp_attribute(__cpp_lib_format)) +#else +#define C4_HAS_STD_FORMAT (0) +#endif +#if C4_HAS_STD_FORMAT +#include <format> +#endif + + +#define BMTHREADS(func) \ + BENCHMARK(func) \ + ->Threads(1) \ + ->Threads(2) \ + ->Threads(3) \ + ->Threads(4) \ + ->Threads(5) \ + ->Threads(6) \ + ->Threads(7) \ + ->Threads(8) \ + ->Threads(9) \ + ->Threads(10) \ + ->UseRealTime() \ + + +void snprintf(bm::State &st) +{ + size_t sum = {}; + char buf[100]; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + int ret = snprintf(buf, sizeof(buf), "%i", i++); + sum += (size_t)ret + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(snprintf); + + +#ifndef __linux__ +void snprintf_l(bm::State &st) +{ + size_t sum = {}; + char buf[100]; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + #if defined(_MSC_VER) + int ret = _snprintf_l(buf, 100, "%i", NULL, i++); + #else + int ret = snprintf_l(buf, 100, NULL, "%i", i++); + #endif + sum += (size_t)ret + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(snprintf_l); +#endif + + +void stb_snprintf(bm::State &st) +{ + size_t sum = {}; + char buf[100]; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + stbsp_snprintf(buf, 100, "%i", i++); + sum += strlen(buf) + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(stb_snprintf); + + + +void sstream(bm::State &st) +{ + size_t sum = {}; + std::stringstream buf; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + buf.seekp(0); + buf << i++; + size_t len = (size_t)buf.tellp(); + buf.seekg(0); + int firstchar = buf.get(); + sum += len + firstchar; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(sstream); + + +void sstream_naive(bm::State &st) +{ + size_t sum = {}; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::stringstream buf; + buf << i++; + std::string ret = buf.str(); + sum += ret.size() + ret[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(sstream_naive); + + +void sstream_naive_reuse(bm::State &st) +{ + size_t sum = {}; + int i = 0; + std::stringstream buf; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + buf.clear(); + buf.str(""); + buf << i++; + std::string ret = buf.str(); + sum += ret.size() + ret[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(sstream_naive_reuse); + + +#ifdef _MSC_VER +void itoa(bm::State &st) +{ + char buf[100]; + size_t sum = {}; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + ::_itoa(i++, buf, 10); + sum += strlen(buf) + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(itoa); +#endif + + +#if C4_HAS_STD_FORMAT +static void std_format_to(bm::State &st) +{ + size_t sum = {}; + char buf[100]; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + const auto res = std::format_to_n(buf, sizeof(buf), /*loc,*/ "{}", i++); + sum += (res.out - buf) + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(std_format_to); +#endif + + +void fmtlib_format_to(bm::State &st) +{ + size_t sum = {}; + fmt::memory_buffer buf; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + buf.clear(); + fmt::format_to(fmt::appender(buf), "{}", i++); + sum += buf.size() + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(fmtlib_format_to); + + +#if C4_HAS_STDTOCHARS +void std_to_chars(bm::State &st) +{ + size_t sum = {}; + char buf[100]; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + std::to_chars_result res = std::to_chars(buf, buf+sizeof(buf), i++); + sum += (res.ptr - buf) + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(std_to_chars); +#endif + + +void c4_write_dec(bm::State &st) +{ + size_t sum = {}; + char buf_[100]; + c4::substr buf = buf_; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + size_t len = c4::write_dec(buf, i++); + sum += len + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(c4_write_dec); + + +void c4_itoa(bm::State &st) +{ + size_t sum = {}; + char buf_[100]; + c4::substr buf = buf_; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + size_t len = c4::itoa(buf, i++); + sum += len + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(c4_itoa); + + +void c4_xtoa(bm::State &st) +{ + size_t sum = {}; + char buf_[100]; + c4::substr buf = buf_; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + size_t len = c4::xtoa(buf, i++); + sum += len + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(c4_xtoa); + + +void c4_to_chars(bm::State &st) +{ + size_t sum = {}; + char buf_[100]; + c4::substr buf = buf_; + int i = 0; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + size_t len = c4::to_chars(buf, i++); + sum += len + buf[0]; + } + } + bm::DoNotOptimize(sum); + report_threadavg<int>(st, kNumValues); +} +BMTHREADS(c4_to_chars); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + bm::Initialize(&argc, argv); + bm::RunSpecifiedBenchmarks(); + return 0; +} diff --git a/thirdparty/ryml/ext/c4core/bm/bm_plot_c4core.py b/thirdparty/ryml/ext/c4core/bm/bm_plot_c4core.py new file mode 100644 index 000000000..23fa55ba3 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/bm_plot_c4core.py @@ -0,0 +1,266 @@ +import sys +import os +import re + +thisdir = os.path.dirname(os.path.abspath(__file__)) +moddir = os.path.abspath(f"{thisdir}/../cmake/bm-xp") +sys.path.insert(0, moddir) +import bm_plot as bm +from bm_util import first + +from dataclasses import dataclass +import prettytable + + +def get_function_benchmark(function_name, run: bm.BenchmarkRun): + for rbm in run.entries: + if rbm.meta.function == function_name: + return rbm + raise Exception(f"function not found: {function_name}. Existing: {[rbm.meta.function for rbm in run.entries]}") + + +@dataclass +class CharconvMeta: # also for atox + title: str + subject: str + function: str + data_type: bm.FundamentalTypes + + @classmethod + def make(cls, bm_title: str): + # eg: + # xtoa_c4_write_dec<uint8_t> + # xtoa_c4_utoa<uint8_t> + # xtoa_c4_xtoa<uint8_t> + # xtoa_c4_to_chars<uint8_t> + # xtoa_std_to_chars<uint8_t> + # xtoa_std_to_string<uint8_t> + # xtoa_sprintf<uint8_t> + # xtoa_sstream_reuse<uint8_t> + # xtoa_sstream<uint8_t> + rx = re.compile(r'(atox|xtoa|xtoahex|xtoaoct|xtoabin)_(.*?)<(u?int\d+_t|float|double)>') + if not rx.fullmatch(bm_title): + raise Exception(f"cannot understand bm title: {bm_title}") + subject = rx.sub(r'\1', bm_title) + function = rx.sub(r'\2', bm_title) + data_type = rx.sub(r'\3', bm_title) + return cls( + title=bm_title, + subject=subject, + function=function.replace("c4_", "c4::").replace("std_", "std::"), + data_type=bm.FundamentalTypes.make(data_type) + ) + + @property + def shortname(self): + return self.function + + @property + def shortparams(self): + return str(self.data_type.short) + + @property + def shorttitle(self): + return f"{self.shortname}<{self.shortparams}>" + + +@dataclass +class CharconvThreadsMeta: + function: str + num_threads: int + + @classmethod + def make(cls, bm_title: str): + # eg: + # c4_itoa/real_time/threads:4 + rx = re.compile(r'(.*?)/real_time/threads:(\d+)') + if not rx.fullmatch(bm_title): + raise Exception(f"cannot understand bm title: {bm_title}") + function = rx.sub(r'\1', bm_title) + num_threads = int(rx.sub(r'\2', bm_title)) + return cls( + function=function.replace("c4_", "c4::").replace("std_", "std::"), + num_threads=num_threads + ) + + def checkbox_groups(self): + return {} + + @property + def shortname(self): + return self.function + + @property + def shorttitle(self): + return self.shortname + + +def plot_charconv_bars(bm_panel: bm.BenchmarkPanel, getref, + panel_title_human: str, + outputfile_prefix: str): + assert os.path.isabs(outputfile_prefix), outputfile_prefix + # make a comparison table + anchor = lambda run: f"{os.path.basename(outputfile_prefix)}-{first(run.meta).data_type}" + anchorlink = lambda run: f"<pre><a href='#{anchor(run)}'>{first(run.meta).data_type}</a></pre>" + with open(f"{outputfile_prefix}.txt", "w") as tablefile: + with open(f"{outputfile_prefix}.md", "w") as mdfile: + print(f"## {panel_title_human}\n\n<p>Data type benchmark results:</p>\n<ul>\n", + "\n".join([f" <li>{anchorlink(run)}</li>" for run in bm_panel.runs]), + "</ul>\n\n", file=mdfile) + for run in bm_panel.runs: + data_type = first(run.meta).data_type + tabletitle = f"{outputfile_prefix}-{data_type.short}" + table = prettytable.PrettyTable(title=f"{panel_title_human}: {data_type}") + table.add_column("function", [m.shorttitle for m in run.meta], align="l") + for prop in ("mega_bytes_per_second", "cpu_time_ms"): + ref = getref(run) + bar_values = list(run.extract_plot_series(prop)) + bar_values_rel = list(run.extract_plot_series(prop, relative_to_entry=ref)) + bar_values_pc = list(run.extract_plot_series(prop, percent_of_entry=ref)) + pd = bm_panel.first_run.property_plot_data(prop) + hns = pd.human_name_short + table.add_column(hns, [f"{v_:7.2f}" for v_ in bar_values], align="r") + hns = hns.replace(" (ms)", "") + table.add_column(f"{hns}(x)", [f"{v_:5.2f}x" for v_ in bar_values_rel], align="r") + table.add_column(f"{hns}(%)", [f"{v_:7.2f}%" for v_ in bar_values_pc], align="r") + print(table, "\n\n") + print(table, "\n\n", file=tablefile) + pfx_bps = f"{os.path.basename(outputfile_prefix)}-mega_bytes_per_second-{data_type.short}" + pfx_cpu = f"{os.path.basename(outputfile_prefix)}-cpu_time_ms-{data_type.short}" + print(f""" +<br/> +<br/> + +--- + +<a id="{anchor(run)}"/> + +### {panel_title_human}: `{data_type}` + +* Interactive html graphs for `{data_type}`: + * [MB/s](./{pfx_bps}.html) + * [CPU time](./{pfx_cpu}.html) + +[](./{pfx_bps}.png) +[](./{pfx_cpu}.png) + +``` +{table} +``` +""", file=mdfile) + # make plots + for prop in ("mega_bytes_per_second", "cpu_time_ms"): + ps, ps_ = [], [] + pd = bm_panel.first_run.property_plot_data(prop) + bar_label = f"{pd.human_name_short}{pd.qty_type.comment}" + outfilename = f"{outputfile_prefix}-{prop}" + for run in bm_panel.runs: + data_type = first(run.meta).data_type + bar_names = [m.shorttitle for m in run.meta] + bar_values = list(run.extract_plot_series(prop)) + runtitle = f"{outfilename}-{data_type.short}" + # to save each bokeh plot separately and also + # a grid plot with all of them, we have to plot + # twice because bokeh does not allow saving twice + # the same plot from multiple pictures. + plotit = lambda: bm.plot_benchmark_run_as_bars(run, title=f"{panel_title_human}: {data_type}\n{bar_label}", + bar_names=bar_names, bar_values=bar_values, bar_label=bar_label) + # make one plot to save: + p, p_ = plotit() + bm._bokeh_save_html(f"{runtitle}.html", p) + bm._plt_save_png(f"{runtitle}.png") + bm._plt_clear() + # and another to gather: + p, p_ = plotit() + ps.append(p) + ps_.append(p_) + bm._plt_clear() + bm.bokeh_plot_many(ps, f"{outfilename}.html") + + +def plot_itoa_threads_(bm_panel: bm.BenchmarkPanel, getref, + panel_title_human: str, + outputfile_prefix: str): + assert os.path.isabs(outputfile_prefix), outputfile_prefix + orig = lambda yprop, **kw: lambda run: list(run.extract_plot_series(yprop, **kw)) + divnt = lambda yprop, **kw: lambda run: [v / n for v, n in run.extract_plot_series_with_threads(yprop, **kw)] + mulnt = lambda yprop, **kw: lambda run: [v * n for v, n in run.extract_plot_series_with_threads(yprop, **kw)] + xprop = "threads" + xpd = bm_panel.first_run.property_plot_data(xprop) + xlabel = f"{xpd.human_name_short}" + for yprop, ylog, yget in ( + #("mega_items_per_second", False, orig), + ("mega_bytes_per_second", False, orig), + #("iterations", False, divnt), + #("real_time_ms", True, mulnt), + ("cpu_time_ms", True, orig),): + ypd = bm_panel.first_run.property_plot_data(yprop) + ylabel = f"{ypd.human_name_short}{ypd.qty_type.comment}" + p = bm.plot_benchmark_panel_as_lines( + bm_panel, f"{panel_title_human}\n{ylabel}", + xget=orig("threads"), + yget=yget(yprop), + nameget=lambda run: first(run.meta).function, + ylog=ylog, + xlabel=xlabel, + ylabel=ylabel + ) + name = f"{outputfile_prefix}-lines-{yprop}" + # save png using matplotlib + bm._plt_save_png(f"{name}.png") + bm._plt_clear() + # save html using bokeh + bm._bokeh_save_html(f"{name}.html", p) + #bkp.show(p) + return p + + +def plot_itoa_threads(dir_: str, json_files): + panel = bm.BenchmarkPanel(json_files, CharconvThreadsMeta) + ref = lambda bmrun: get_function_benchmark("std::to_chars", run=bmrun) + plot_itoa_threads_(panel, ref, + f"itoa benchmark: convert 2M 32b integers to string", + f"{dir_}/c4core-bm-charconv_threads") + + +def plot_charconv_xtoa(dir_: str, json_files, is_ftoa: bool): + fcase = "ftoa" if is_ftoa else "xtoa" + panel = bm.BenchmarkPanel(json_files, CharconvMeta) + ref = lambda bmrun: get_function_benchmark("sprintf", run=bmrun) + plot_charconv_bars(panel, ref, + f"xtoa benchmark: convert 1M numbers to strings", + f"{dir_}/c4core-bm-charconv-{fcase}") + + +def plot_charconv_atox(dir_: str, json_files, is_atof: bool): + fcase = "atof" if is_atof else "atox" + panel = bm.BenchmarkPanel(json_files, CharconvMeta) + ref = lambda bmrun: get_function_benchmark("scanf", run=bmrun) + plot_charconv_bars(panel, ref, + f"atox benchmark: convert 1M strings to numbers", + f"{dir_}/c4core-bm-charconv-{fcase}") + + +if __name__ == '__main__': + args = sys.argv[1:] + if len(args) < 2: + raise Exception(f"usage: {sys.executable} {sys.argv[0]} <atox|xtoa|itoa_threads|format|digits> benchmarkfile.json[,benchmarkfile2.json,...]") + cmd = args[0] + json_files = args[1:] + dir_ = os.path.dirname(json_files[0]) + for jf in json_files: + print("jf:", jf, flush=True) + assert os.path.dirname(jf) == dir_, (os.path.dirname(jf), dir_) + assert os.path.exists(jf), jf + if cmd == "itoa_threads": + plot_itoa_threads(dir_, json_files) + elif cmd == "xtoa" or cmd == "ftoa": + plot_charconv_xtoa(dir_, json_files, (cmd == "ftoa")) + elif cmd == "atox" or cmd == "atof": + plot_charconv_atox(dir_, json_files, (cmd == "atof")) + elif cmd == "format": + raise Exception(f"not implemented: {cmd}") + elif cmd == "digits": + pass # nothing to do + else: + raise Exception(f"not implemented: {cmd}") diff --git a/thirdparty/ryml/ext/c4core/bm/bm_xtoa.cpp b/thirdparty/ryml/ext/c4core/bm/bm_xtoa.cpp new file mode 100644 index 000000000..681e443c6 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/bm_xtoa.cpp @@ -0,0 +1,1538 @@ +#include "./bm_charconv.hpp" +#include <c4/error.hpp> +#include <iostream> + + +// this is an exploratory benchmark to compare the possible +// combinations for all the components of the write_dec() algorithm + + +template<class T> using msb_func = unsigned (*)(T val); + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# if __GNUC__ >= 8 +# pragma GCC diagnostic ignored "-Wstringop-truncation" +# pragma GCC diagnostic ignored "-Wstringop-overflow" +# endif +#endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +// WIP. +// TODO: _BitscanReverse() in MSVC +C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wuseless-cast") +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto msb_intrinsic(unsigned v) noexcept + -> typename std::enable_if<__has_builtin(__builtin_clz), unsigned>::type +{ + using I = unsigned; + enum : I { total = (I)(I(8) * sizeof(I) - 1) }; + return (total - (I) __builtin_clz(v)); +} + +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto msb_intrinsic_64bit(unsigned long v) noexcept +-> typename std::enable_if<__has_builtin(__builtin_clzl), unsigned>::type +{ + using I = unsigned long; + enum : I { total = (I)(I(8) * sizeof(I) - 1) }; + return (unsigned)(total - (I) __builtin_clzl(v)); +} + +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto msb_intrinsic_64bit(unsigned long long v) noexcept + -> typename std::enable_if<__has_builtin(__builtin_clzll), unsigned>::type +{ + using I = unsigned long long; + enum : I { total = (I)(I(8) * sizeof(I) - 1) }; + return (unsigned)(total - (I) __builtin_clzll(v)); +} +C4_SUPPRESS_WARNING_GCC_POP + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned msb_loop(I v) noexcept +{ + // https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10Obvious + unsigned r = 0; + while (v >>= 1) // unroll for more speed... + r++; + return r; +} + +// https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup +constexpr static const int8_t LogTable256[256] = { +#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n + -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6), + LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7) +#undef LT +}; +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +unsigned msb_de_bruijn_32bit(I v) noexcept +{ + I t, tt; // temporaries + tt = v >> 16; + if (tt) + return (unsigned)((t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt]); + return (unsigned)((t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v]); +} + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +unsigned msb_intrinsic_8bit(I v) noexcept +{ + return msb_intrinsic((unsigned)v); +} + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +unsigned msb_divconq_8bit(I v) noexcept +{ + C4_STATIC_ASSERT(sizeof(I) == 1); + unsigned n = 0; + if(v & I(0xf0)) v >>= 4, n |= I(4); + if(v & I(0x0c)) v >>= 2, n |= I(2); + if(v & I(0x02)) v >>= 1, n |= I(1); + return n; +} + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned msb_intrinsic_16bit(I v) noexcept +{ + return msb_intrinsic((unsigned)v); +} + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned msb_divconq_16bit(I v) noexcept +{ + C4_STATIC_ASSERT(sizeof(I) == 2); + unsigned n = 0; + if(v & I(0xff00)) v >>= 8, n |= I(8); + if(v & I(0x00f0)) v >>= 4, n |= I(4); + if(v & I(0x000c)) v >>= 2, n |= I(2); + if(v & I(0x0002)) v >>= 1, n |= I(1); + return n; +} + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned msb_intrinsic_32bit(I v) noexcept +{ + return msb_intrinsic((unsigned)v); +} + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned msb_divconq_32bit(I v) noexcept +{ + C4_STATIC_ASSERT(sizeof(I) == 4); + unsigned n = 0; + if(v & I(0xffff0000)) v >>= 16, n |= I(16); + if(v & I(0x0000ff00)) v >>= 8, n |= I(8); + if(v & I(0x000000f0)) v >>= 4, n |= I(4); + if(v & I(0x0000000c)) v >>= 2, n |= I(2); + if(v & I(0x00000002)) v >>= 1, n |= I(1); + return n; +} + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned msb_divconq_branchless_32bit(I v) noexcept +{ + C4_STATIC_ASSERT(sizeof(I) == 4); + unsigned r = (unsigned)(v > 0xFFFF) << 4; v >>= r; + unsigned shift = (unsigned)(v > 0xFF ) << 3; v >>= shift; r |= shift; + shift = (unsigned)(v > 0xF ) << 2; v >>= shift; r |= shift; + shift = (unsigned)(v > 0x3 ) << 1; v >>= shift; r |= shift; + r |= (unsigned)(v >> 1); + return r; +} + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned msb_intrinsic_64bit(I v) noexcept +{ + return msb_intrinsic_64bit((uint64_t)v); +} + +template<class I> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned msb_divconq_64bit(I v) noexcept +{ + C4_STATIC_ASSERT(sizeof(I) == 8); + unsigned n = 0; + if(v & I(0xffffffff00000000)) v >>= 32, n |= I(32); + if(v & I(0x00000000ffff0000)) v >>= 16, n |= I(16); + if(v & I(0x000000000000ff00)) v >>= 8, n |= I(8); + if(v & I(0x00000000000000f0)) v >>= 4, n |= I(4); + if(v & I(0x000000000000000c)) v >>= 2, n |= I(2); + if(v & I(0x0000000000000002)) v >>= 1, n |= I(1); + return n; +} + + +template<class T, msb_func<T> msbfunc> +void msb(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += msbfunc(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + +C4BM_TEMPLATE(msb, uint8_t, msb_intrinsic_8bit<uint8_t>); +C4BM_TEMPLATE(msb, uint8_t, msb_divconq_8bit<uint8_t>); +C4BM_TEMPLATE(msb, uint8_t, msb_loop<uint8_t>); + +C4BM_TEMPLATE(msb, int8_t, msb_intrinsic_8bit<int8_t>); +C4BM_TEMPLATE(msb, int8_t, msb_divconq_8bit<int8_t>); +C4BM_TEMPLATE(msb, int8_t, msb_loop<int8_t>); + +C4BM_TEMPLATE(msb, uint16_t, msb_intrinsic_16bit<uint16_t>); +C4BM_TEMPLATE(msb, uint16_t, msb_divconq_16bit<uint16_t>); +C4BM_TEMPLATE(msb, uint16_t, msb_loop<uint16_t>); + +C4BM_TEMPLATE(msb, int16_t, msb_intrinsic_16bit<int16_t>); +C4BM_TEMPLATE(msb, int16_t, msb_divconq_16bit<int16_t>); +C4BM_TEMPLATE(msb, int16_t, msb_loop<int16_t>); + +C4BM_TEMPLATE(msb, uint32_t, msb_intrinsic_32bit<uint32_t>); +C4BM_TEMPLATE(msb, uint32_t, msb_de_bruijn_32bit<uint32_t>); +C4BM_TEMPLATE(msb, uint32_t, msb_divconq_32bit<uint32_t>); +C4BM_TEMPLATE(msb, uint32_t, msb_divconq_branchless_32bit<uint32_t>); +C4BM_TEMPLATE(msb, uint32_t, msb_loop<uint32_t>); + +C4BM_TEMPLATE(msb, int32_t, msb_intrinsic_32bit<int32_t>); +C4BM_TEMPLATE(msb, int32_t, msb_de_bruijn_32bit<int32_t>); +C4BM_TEMPLATE(msb, int32_t, msb_divconq_32bit<int32_t>); +C4BM_TEMPLATE(msb, int32_t, msb_divconq_branchless_32bit<int32_t>); +C4BM_TEMPLATE(msb, int32_t, msb_loop<int32_t>); + +C4BM_TEMPLATE(msb, uint64_t, msb_intrinsic_64bit<uint64_t>); +C4BM_TEMPLATE(msb, uint64_t, msb_divconq_64bit<uint64_t>); +C4BM_TEMPLATE(msb, uint64_t, msb_loop<uint64_t>); + +C4BM_TEMPLATE(msb, int64_t, msb_intrinsic_64bit<int64_t>); +C4BM_TEMPLATE(msb, int64_t, msb_divconq_64bit<int64_t>); +C4BM_TEMPLATE(msb, int64_t, msb_loop<int64_t>); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace impl { + +template<typename _Tp, unsigned __base> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_glibc(_Tp __value) noexcept + -> typename std::enable_if<std::is_unsigned<_Tp>::value, unsigned>::type +{ + static_assert(std::is_integral<_Tp>::value, "implementation bug"); + static_assert(std::is_unsigned<_Tp>::value, "implementation bug"); + unsigned __n = 1; + const unsigned __b2 = __base * __base; + const unsigned __b3 = __b2 * __base; + const unsigned long __b4 = __b3 * __base; + for (;;) + { + if (__value < (unsigned)__base) return __n; + if (__value < __b2) return __n + 1; + if (__value < __b3) return __n + 2; + if (__value < __b4) return __n + 3; + __value /= (_Tp) __b4; + __n += 4; + } +} + +template<typename _Tp> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_glibc(_Tp __value, unsigned __base) noexcept + -> typename std::enable_if<std::is_unsigned<_Tp>::value, unsigned>::type +{ + static_assert(std::is_integral<_Tp>::value, "implementation bug"); + static_assert(std::is_unsigned<_Tp>::value, "implementation bug"); + unsigned __n = 1; + const unsigned __b2 = __base * __base; + const unsigned __b3 = __b2 * __base; + const unsigned long __b4 = __b3 * __base; + for (;;) + { + if (__value < (unsigned)__base) return __n; + if (__value < __b2) return __n + 1; + if (__value < __b3) return __n + 2; + if (__value < __b4) return __n + 3; + __value /= (_Tp)__b4; + __n += 4; + } +} + +template<typename _Tp, unsigned __base> +constexpr C4_ALWAYS_INLINE +auto digits_glibc(_Tp __value) noexcept + -> typename std::enable_if<std::is_signed<_Tp>::value, unsigned>::type +{ + using U = typename std::make_unsigned<_Tp>::type; + return digits_glibc<U, __base>((U)__value); +} + +template<typename _Tp> +constexpr C4_ALWAYS_INLINE +auto digits_glibc(_Tp __value, unsigned __base) noexcept + -> typename std::enable_if<std::is_signed<_Tp>::value, unsigned>::type +{ + using U = typename std::make_unsigned<_Tp>::type; + return digits_glibc<U>((U)__value, __base); +} + + +//------------------------------------------- +// https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10Obvious + +// these functions assume the numbers are positive even when the type +// is signed + +template<class T> +constexpr C4_ALWAYS_INLINE +auto digits_dec_naive_hifirst(T v) noexcept + -> typename std::enable_if<sizeof(T)==1u,unsigned>::type +{ + // best when the numbers are uniformly distributed over the whole range + return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u)); +} + +template<class T> +constexpr C4_ALWAYS_INLINE +auto digits_dec_naive_lofirst(T v) noexcept + -> typename std::enable_if<sizeof(T)==1u,unsigned>::type +{ + // best when lower numbers are more likely + return ((v < 10) ? 1u : ((v < 100) ? 2u : 3u)); +} + + +// 16 bit + +template<class T> +constexpr C4_ALWAYS_INLINE +auto digits_dec_naive_hifirst(T v) noexcept + -> typename std::enable_if<sizeof(T)==2u,unsigned>::type +{ + // best when the numbers are uniformly distributed over the whole range + return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); +} + +template<class T> +constexpr C4_ALWAYS_INLINE +auto digits_dec_naive_lofirst(T v) noexcept + -> typename std::enable_if<sizeof(T)==2u,unsigned>::type +{ + // best when lower numbers are more likely + return ((v < 100) ? ((v >= 10) ? 2u : 1u) : ((v < 1000) ? 3u : ((v < 10000) ? 4u : 5u))); +} + + +// 32 bit + +template<class T> +constexpr C4_ALWAYS_INLINE +auto digits_dec_naive_hifirst(T v) noexcept + -> typename std::enable_if<sizeof(T)==4u,unsigned>::type +{ + return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u : + (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u : + (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); +} + +template<class T> +constexpr C4_ALWAYS_INLINE +auto digits_dec_naive_lofirst(T v) noexcept + -> typename std::enable_if<sizeof(T)==4u,unsigned>::type +{ + return ((v < 10) ? 1u : (v < 100) ? 2u : (v < 1000) ? 3u : (v < 10000) ? 4u : + (v < 100000) ? 5u : (v < 1000000) ? 6u : (v < 10000000) ? 7u : + (v < 100000000) ? 8u : (v < 1000000000) ? 9u : 10u); +} + + +// 64 bit + +template<class T> +constexpr C4_ALWAYS_INLINE +auto digits_dec_naive_hifirst(T v) noexcept + -> typename std::enable_if<sizeof(T)==8u,unsigned>::type +{ + return ((std::is_unsigned<T>::value && v >= T(10000000000000000000u)) ? 20u : + (v >= 1000000000000000000) ? 19u : (v >= 100000000000000000) ? 18u : (v >= 10000000000000000) ? 17u : + (v >= 1000000000000000) ? 16u : (v >= 100000000000000) ? 15u : (v >= 10000000000000) ? 14u : + (v >= 1000000000000) ? 13u : (v >= 100000000000) ? 12u : (v >= 10000000000) ? 11u : + (v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u : + (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u : + (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); +} + +template<class T> +constexpr C4_ALWAYS_INLINE +auto digits_dec_naive_lofirst(T v) noexcept + -> typename std::enable_if<sizeof(T)==8u,unsigned>::type +{ + return ((v < 10) ? 1u : (v < 100) ? 2u : (v < 1000) ? 3u : + (v < 10000) ? 4u : (v < 100000) ? 5u : (v < 1000000) ? 6u : + (v < 10000000) ? 7u : (v < 100000000) ? 8u : (v < 1000000000) ? 9u : + (v < 10000000000) ? 10u : (v < 100000000000) ? 11u : (v < 1000000000000) ? 12u : + (v < 10000000000000) ? 13u : (v < 100000000000000) ? 14u : (v < 1000000000000000) ? 15u : + (v < 10000000000000000) ? 16u : (v < 100000000000000000) ? 17u : (v < 1000000000000000000) ? 18u : + ((typename std::make_unsigned<T>::type)v < 10000000000000000000u) ? 19u : 20u); +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec_naive_hifirst64fallback32(T v) noexcept + -> typename std::enable_if<sizeof(T)==8u,unsigned>::type +{ + if(v >= std::numeric_limits<uint32_t>::max()) + return digits_glibc<T, 10u>(v); + else + return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u : + (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u : + (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec_naive_lofirst64fallback32(T v) noexcept + -> typename std::enable_if<sizeof(T)==8u,unsigned>::type +{ + if(v < std::numeric_limits<uint32_t>::max()) + return ((v < 10) ? 1u : (v < 100) ? 2u : (v < 1000) ? 3u : (v < 10000) ? 4u : + (v < 100000) ? 5u : (v < 1000000) ? 6u : (v < 10000000) ? 7u : + (v < 100000000) ? 8u : (v < 1000000000) ? 9u : 10u); + else + return digits_glibc<T, 10u>(v); +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec_naive_fargies(T v) noexcept + -> typename std::enable_if<sizeof(T)==8u,unsigned>::type +{ + // https://github.com/biojppm/c4core/pull/77#issuecomment-1063753568 + if(v >= 1000000000) // 10 + { + if(v >= 100000000000000) // 15 [15-20] range + { + if(v >= 100000000000000000) // 18 (15 + (20 - 15) / 2) + { + if((typename std::make_unsigned<T>::type)v >= 10000000000000000000u) // 20 + return 20u; + else + return (v >= 1000000000000000000) ? 19u : 18u; + } + else if(v >= 10000000000000000) // 17 + return 17u; + else + return(v >= 1000000000000000) ? 16u : 15u; + } + else if(v >= 1000000000000) // 13 + return (v >= 10000000000000) ? 14u : 13u; + else if(v >= 100000000000) // 12 + return 12; + else + return(v >= 10000000000) ? 11u : 10u; + } + else if(v >= 10000) // 5 [5-9] range + { + if(v >= 10000000) // 8 + return (v >= 100000000) ? 9u : 8u; + else if(v >= 1000000) // 7 + return 7; + else + return (v >= 100000) ? 6u : 5u; + } + else if(v >= 100) + return (v >= 1000) ? 4u : 3u; + else + return (v >= 10) ? 2u : 1u; +} + + +//------------------------------------------- +namespace c4 { +namespace detail { +template<class T, typename=void> +struct powers_of_10; + +#define _C4_POWERS_OF_10_FOR(cond, ...) \ +template<class T> \ +struct powers_of_10<T, typename std::enable_if<cond, void>::type> \ +{ \ + static C4_INLINE_CONSTEXPR const T values[] = {__VA_ARGS__}; \ + static C4_INLINE_CONSTEXPR const T values_size = C4_COUNTOF(values); \ +}; \ +template<class T> \ +C4_INLINE_CONSTEXPR const T powers_of_10<T, typename std::enable_if<cond, void>::type>::values[] + +_C4_POWERS_OF_10_FOR(sizeof(T)==1u, 1, 10, 100 ); +_C4_POWERS_OF_10_FOR(sizeof(T)==2u, 1, 10, 100, 1000, 10000 ); +_C4_POWERS_OF_10_FOR(sizeof(T)==4u, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 ); +_C4_POWERS_OF_10_FOR(std::is_signed<T>::value && + sizeof(T)==8u, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000 ); +_C4_POWERS_OF_10_FOR(std::is_unsigned<T>::value && + sizeof(T)==8u, 1u, 10u, 100u, 1000u, 10000u, 100000u, 1000000u, 10000000u, 100000000u, 1000000000u, 10000000000u, 100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u, 10000000000000000u, 100000000000000000u, 1000000000000000000u, 10000000000000000000u ); +} // namespace detail +} // namespace c4 + + +template<class T, msb_func<T> msbfunc> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +unsigned digits_dec_log10_nocheck(T v) noexcept +{ + // https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + const unsigned mag = ((unsigned)msbfunc(v) + 1u) * 1233u >> 12; + C4_ASSERT(mag < c4::detail::powers_of_10<T>::values_size); + return 1u + mag - (v < c4::detail::powers_of_10<T>::values[mag]); +} + +template<class T, msb_func<T> msbfunc> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +unsigned digits_dec_log10(T v) noexcept +{ + if(v) + { + // https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + const unsigned mag = ((unsigned)msbfunc(v) + 1u) * 1233u >> 12; + C4_ASSERT(mag < c4::detail::powers_of_10<T>::values_size); + return 1u + mag - (v < c4::detail::powers_of_10<T>::values[mag]); + } + return 1u; +} + +} // namespace impl + + +template<class T> +void digits_dec_naive_fargies(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += impl::digits_dec_naive_fargies(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +template<class T> +void digits_dec_naive_hifirst(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += impl::digits_dec_naive_hifirst(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +template<class T> +void digits_dec_naive_lofirst(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += impl::digits_dec_naive_lofirst(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +template<class T> +void digits_dec_naive_hifirst64fallback32(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += impl::digits_dec_naive_hifirst64fallback32(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +template<class T> +void digits_dec_naive_lofirst64fallback32(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += impl::digits_dec_naive_lofirst64fallback32(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + + +template<class T> +void digits_dec_glibc_tpl(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += impl::digits_glibc<T, 10>(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +template<class T> +void digits_dec_glibc(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += impl::digits_glibc(values.next(), 10); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +template<class T, msb_func<T> msbfunc> +void digits_dec_log10_nocheck(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += impl::digits_dec_log10_nocheck<T, msbfunc>(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +template<class T, msb_func<T> msbfunc> +void digits_dec_log10(bm::State &st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + unsigned sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += impl::digits_dec_log10<T, msbfunc>(values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +#define C4_INSTANTIATE_DIGITS_DEC_BENCHMARKS(ty, num) \ +C4BM_TEMPLATE(digits_dec_naive_hifirst, ty); \ +C4BM_TEMPLATE(digits_dec_naive_lofirst, ty); \ +C4BM_TEMPLATE(digits_dec_log10_nocheck, ty, msb_intrinsic_##num##bit<ty>); \ +C4BM_TEMPLATE(digits_dec_log10_nocheck, ty, msb_divconq_##num##bit<ty>); \ +C4BM_TEMPLATE(digits_dec_log10_nocheck, ty, msb_loop<ty>); \ +C4BM_TEMPLATE(digits_dec_log10, ty, msb_intrinsic_##num##bit<ty>); \ +C4BM_TEMPLATE(digits_dec_log10, ty, msb_divconq_##num##bit<ty>); \ +C4BM_TEMPLATE(digits_dec_log10, ty, msb_loop<ty>); \ +C4BM_TEMPLATE(digits_dec_glibc_tpl, ty); \ +C4BM_TEMPLATE(digits_dec_glibc, ty) + + +C4_INSTANTIATE_DIGITS_DEC_BENCHMARKS(uint8_t, 8); +C4_INSTANTIATE_DIGITS_DEC_BENCHMARKS(int8_t, 8); +C4_INSTANTIATE_DIGITS_DEC_BENCHMARKS(uint16_t, 16); +C4_INSTANTIATE_DIGITS_DEC_BENCHMARKS(int16_t, 16); +C4_INSTANTIATE_DIGITS_DEC_BENCHMARKS(uint32_t, 32); +C4_INSTANTIATE_DIGITS_DEC_BENCHMARKS(int32_t, 32); +C4BM_TEMPLATE(digits_dec_naive_fargies, uint64_t); +C4BM_TEMPLATE(digits_dec_naive_hifirst64fallback32, uint64_t); +C4BM_TEMPLATE(digits_dec_naive_lofirst64fallback32, uint64_t); +C4_INSTANTIATE_DIGITS_DEC_BENCHMARKS(uint64_t, 64); +C4BM_TEMPLATE(digits_dec_naive_fargies, int64_t); +C4BM_TEMPLATE(digits_dec_naive_hifirst64fallback32, int64_t); +C4BM_TEMPLATE(digits_dec_naive_lofirst64fallback32, int64_t); +C4_INSTANTIATE_DIGITS_DEC_BENCHMARKS(int64_t, 64); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace impl { + +// LEGEND: + +// checkall: check buffer length on every insertion +// checkonce: check buffer length only once when entering the function +// checkoncemax: as above, and compare against a compile-time maxdigits for the type +// checkoncelog: as above, and compare against the exact digits each from the actual number + +// divrem: compute div with operator/ and rem with operator% +// singlediv: compute div with operator/ but rem without using operator% (explicitly compute the remainder) + +// write1: write 1 digit per division (divide by 10 on each step) +// write2: write 2 digits per division (divide by 100 on each step) + + +static constexpr const char digits0099[201] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + + +//------------------------------------------------------------------- + +template<class T> +C4_ALWAYS_INLINE size_t write_dec_checkall_divrem_write1(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + size_t pos = 0; + do { + if(C4_LIKELY(pos < buf.len)) + buf.str[pos] = (char)('0' + (v % T(10))); + ++pos; + v /= T(10); + } while(v); + buf.reverse_range(0, pos); + return pos; +} + +template<class T> +C4_ALWAYS_INLINE size_t write_dec_checkall_divrem_write2(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + size_t pos = 0; + while(v >= T(100)) + { + const auto num = (v % T(100)) << 1u; + v /= T(100); + if(C4_LIKELY(pos + 2 < buf.len)) + { + buf.str[pos++] = digits0099[num + 1]; + buf.str[pos++] = digits0099[num]; + } + } + if(v >= T(10)) + { + const auto num = v << 1u; + if(C4_LIKELY(pos + 2 < buf.len)) + { + buf.str[pos++] = digits0099[num + 1]; + buf.str[pos++] = digits0099[num]; + } + } + else + { + if(C4_LIKELY(pos < buf.len)) + buf.str[pos++] = (char)('0' + v); + } + buf.reverse_range(0, pos); + return pos; +} + + +//------------------------------------------------------------------- + +template<class T> +C4_ALWAYS_INLINE size_t write_dec_checkall_singlediv_write1(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + size_t pos = 0; + do { + const T quo = v / T(10); + const auto rem = v - quo * T(10); + v = quo; + if(C4_LIKELY(pos < buf.len)) + buf.str[pos] = (char)('0' + rem); + ++pos; + } while(v); + buf.reverse_range(0, pos); + return pos; +} + +template<class T> +C4_ALWAYS_INLINE size_t write_dec_checkall_singlediv_write2(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + size_t pos = 0; + while(v >= T(100)) + { + const T quo = v / T(100); + const auto num = (v - quo * T(100)) << 1u; + v = quo; + if(C4_LIKELY(pos+2 < buf.len)) + { + buf.str[pos++] = digits0099[num + 1]; + buf.str[pos++] = digits0099[num]; + } + } + if(v >= T(10)) + { + const auto num = v << 1u; + if(C4_LIKELY(pos+2 < buf.len)) + { + buf.str[pos++] = digits0099[num + 1]; + buf.str[pos++] = digits0099[num ]; + } + } + else + { + if(C4_LIKELY(pos < buf.len)) + buf.str[pos++] = (char)('0' + v); + } + buf.reverse_range(0, pos); + return pos; +} + + +//------------------------------------------------------------------- + +template<class T> +C4_ALWAYS_INLINE size_t write_dec_checkoncemax_divrem_write1(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + if(C4_UNLIKELY(buf.len < c4::detail::charconv_digits<T>::maxdigits_dec)) + return c4::detail::charconv_digits<T>::maxdigits_dec; + size_t pos = 0; + do { + buf.str[pos++] = (char)('0' + (v % T(10))); + v /= T(10); + } while(v); + buf.reverse_range(0, pos); + return pos; +} + +template<class T> +C4_ALWAYS_INLINE size_t write_dec_checkoncemax_divrem_write2(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + if(C4_UNLIKELY(buf.len < c4::detail::charconv_digits<T>::maxdigits_dec)) + return c4::detail::charconv_digits<T>::maxdigits_dec; + size_t pos = 0; + while(v >= T(100)) + { + const auto num = (v % T(100)) << 1u; + v /= T(100); + buf.str[pos++] = digits0099[num + 1]; + buf.str[pos++] = digits0099[num]; + } + if(v >= T(10)) + { + const auto num = v << 1u; + buf.str[pos++] = digits0099[num + 1]; + buf.str[pos++] = digits0099[num ]; + } + else + { + buf.str[pos++] = (char)('0' + v); + } + buf.reverse_range(0, pos); + return pos; +} + + +//------------------------------------------------------------------- + +template<class T> +C4_ALWAYS_INLINE size_t write_dec_checkoncemax_singlediv_write1(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + if(C4_UNLIKELY(buf.len < c4::detail::charconv_digits<T>::maxdigits_dec)) + return c4::detail::charconv_digits<T>::maxdigits_dec; + size_t pos = 0; + do { + const T quo = v / T(10); + const auto rem = (v - quo * T(10)); + v = quo; + buf.str[pos++] = (char)('0' + rem); + } while(v); + buf.reverse_range(0, pos); + return pos; +} + +template<class T> +C4_ALWAYS_INLINE size_t write_dec_checkoncemax_singlediv_write2(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + size_t pos = 0; + if(C4_UNLIKELY(buf.len < c4::detail::charconv_digits<T>::maxdigits_dec)) + return c4::detail::charconv_digits<T>::maxdigits_dec; + while(v >= T(100)) + { + const T quo = v / T(100); + const auto num = (v - quo * T(100)) << 1u; + v = quo; + buf.str[pos++] = digits0099[num + 1]; + buf.str[pos++] = digits0099[num]; + } + if(v >= T(10)) + { + const auto num = v << 1u; + buf.str[pos++] = digits0099[num + 1]; + buf.str[pos++] = digits0099[num ]; + } + else + { + buf.str[pos++] = (char)('0' + v); + } + buf.reverse_range(0, pos); + return pos; +} + + +//------------------------------------------------------------------- + +template<class T, msb_func<T> digitsfunc> +C4_ALWAYS_INLINE size_t write_dec_checkoncelog_divrem_write1(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + unsigned digits = digitsfunc(v); + if(C4_UNLIKELY(buf.len < digits)) + return digits; + size_t pos = digits; + do { + buf.str[--pos] = (char)('0' + (v % T(10))); + v /= T(10); + } while(v); + return digits; +} + +template<class T, msb_func<T> digitsfunc> +C4_ALWAYS_INLINE size_t write_dec_checkoncelog_divrem_write2(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + unsigned digits = digitsfunc(v); + if(C4_UNLIKELY(buf.len < digits)) + return digits; + size_t pos = digits; + while(v >= T(100)) + { + const auto num = (v % T(100)) << 1u; + v /= T(100); + buf.str[--pos] = digits0099[num + 1]; + buf.str[--pos] = digits0099[num]; + } + if(v >= T(10)) + { + const auto num = v << 1u; + buf.str[1] = digits0099[num + 1]; + buf.str[0] = digits0099[num]; + } + else + { + buf.str[0] = (char)('0' + v); + } + return digits; +} + + +//------------------------------------------------------------------- + +template<class T, msb_func<T> digitsfunc> +C4_ALWAYS_INLINE size_t write_dec_checkoncelog_singlediv_write1(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + unsigned digits = digitsfunc(v); + if(C4_UNLIKELY(buf.len < digits)) + return digits; + size_t pos = digits; + do { + const T quo = v / T(10); + const auto rem = (v - quo * T(10)); + v = quo; + buf.str[--pos] = (char)('0' + rem); + } while(v); + return digits; +} + +template<class T, msb_func<T> digitsfunc> +C4_ALWAYS_INLINE size_t write_dec_checkoncelog_singlediv_write2(c4::substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + unsigned digits = digitsfunc(v); + if(C4_UNLIKELY(buf.len < digits)) + return digits; + size_t pos = digits; + while(v >= T(100)) + { + const T quo = v / T(100); + const auto num = (v - quo * T(100)) << 1u; + v = quo; + buf.str[--pos] = digits0099[num + 1]; + buf.str[--pos] = digits0099[num]; + } + if(v >= T(10)) + { + const auto num = v << 1u; + buf.str[1] = digits0099[num + 1]; + buf.str[0] = digits0099[num ]; + } + else + { + buf.str[0] = (char)('0' + v); + } + return digits; +} +} // namespace impl + + + +#define C4_DEFINE_WRITE_DEC_BM(name) \ +template<class T> \ +void write_dec_##name(bm::State &st) \ +{ \ + random_values_cref<T> values = mkvals_positive<T>(); \ + string_buffer buf_ = {}; \ + c4::substr buf = buf_; \ + C4_ASSERT(buf.len > 11); \ + size_t sum = {}; \ + for(auto _ : st) \ + { \ + C4DOALL(kNumValues) \ + sum += impl::write_dec_##name<T>(buf, values.next()); \ + } \ + bm::DoNotOptimize(sum); \ + report<T>(st, kNumValues); \ +} + +#define C4_DEFINE_WRITE_DEC_BM_FUNC(name) \ +template<class T, msb_func<T> msbfunc> \ +void write_dec_##name(bm::State &st) \ +{ \ + random_values_cref<T> values = mkvals_positive<T>(); \ + string_buffer buf_ = {}; \ + c4::substr buf = buf_; \ + C4_ASSERT(buf.len > 11); \ + size_t sum = {}; \ + for(auto _ : st) \ + { \ + C4DOALL(kNumValues) \ + sum += impl::write_dec_##name<T, msbfunc>(buf, values.next()); \ + } \ + bm::DoNotOptimize(sum); \ + report<T>(st, kNumValues); \ +} + + +C4FOR(T, isint) +write_dec_c4_write_dec(bm::State& st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + string_buffer buf_ = {}; + c4::substr buf = buf_; + size_t sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + sum += c4::write_dec(buf, values.next()); + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} + +#if defined(__cpp_lib_to_chars) || (C4_CPP >= 17) +#define C4_TO_CHARS_BM(ty) C4BM_TEMPLATE(write_dec_std_to_chars, ty) +C4FOR(T, isint) +write_dec_std_to_chars(bm::State& st) +{ + random_values_cref<T> values = mkvals_positive<T>(); + string_buffer buf_ = {}; + c4::substr buf = buf_; + size_t sum = {}; + for(auto _ : st) + { + C4DOALL(kNumValues) + { + auto result = std::to_chars(buf.begin(), buf.end(), values.next()); + sum += (size_t)(result.ptr - buf.str); + } + } + bm::DoNotOptimize(sum); + report<T>(st, kNumValues); +} +#else +#define C4_TO_CHARS_BM(ty) +#endif + + +C4_DEFINE_WRITE_DEC_BM_FUNC(checkoncelog_singlediv_write2) +C4_DEFINE_WRITE_DEC_BM_FUNC(checkoncelog_singlediv_write1) +C4_DEFINE_WRITE_DEC_BM_FUNC(checkoncelog_divrem_write2) +C4_DEFINE_WRITE_DEC_BM_FUNC(checkoncelog_divrem_write1) + +C4_DEFINE_WRITE_DEC_BM(checkoncemax_singlediv_write2) +C4_DEFINE_WRITE_DEC_BM(checkoncemax_singlediv_write1) +C4_DEFINE_WRITE_DEC_BM(checkoncemax_divrem_write2) +C4_DEFINE_WRITE_DEC_BM(checkoncemax_divrem_write1) + +C4_DEFINE_WRITE_DEC_BM(checkall_singlediv_write2) +C4_DEFINE_WRITE_DEC_BM(checkall_singlediv_write1) +C4_DEFINE_WRITE_DEC_BM(checkall_divrem_write2) +C4_DEFINE_WRITE_DEC_BM(checkall_divrem_write1) + + + +#define C4_INSTANTIATE_WRITE_DEC_BENCHMARKS(ty, num) \ + \ +/*compare against std::to_chars()*/ \ +C4_TO_CHARS_BM(ty); \ + \ +/*our versions*/ \ +C4BM_TEMPLATE(write_dec_c4_write_dec, ty); \ + \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_naive_hifirst<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_naive_lofirst<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_glibc<ty, 10u>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_intrinsic_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_divconq_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_loop<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_log10<ty C4_COMMA msb_intrinsic_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_log10<ty C4_COMMA msb_divconq_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_log10<ty C4_COMMA msb_loop<ty>>); \ + \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_naive_hifirst<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_naive_lofirst<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_glibc<ty C4_COMMA 10u>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_intrinsic_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_divconq_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_loop<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_log10<ty C4_COMMA msb_intrinsic_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_log10<ty C4_COMMA msb_divconq_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_log10<ty C4_COMMA msb_loop<ty>>); \ + \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_naive_hifirst<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_naive_lofirst<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_glibc<ty C4_COMMA 10u>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_intrinsic_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_divconq_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_loop<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_log10<ty C4_COMMA msb_intrinsic_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_log10<ty C4_COMMA msb_divconq_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_log10<ty C4_COMMA msb_loop<ty>>); \ + \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_naive_hifirst<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_naive_lofirst<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_glibc<ty C4_COMMA 10u>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_intrinsic_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_divconq_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_log10_nocheck<ty C4_COMMA msb_loop<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_log10<ty C4_COMMA msb_intrinsic_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_log10<ty C4_COMMA msb_divconq_##num##bit<ty>>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_log10<ty C4_COMMA msb_loop<ty>>); \ + \ +C4BM_TEMPLATE(write_dec_checkoncemax_singlediv_write2, ty); \ +C4BM_TEMPLATE(write_dec_checkoncemax_singlediv_write1, ty); \ +C4BM_TEMPLATE(write_dec_checkoncemax_divrem_write2, ty); \ +C4BM_TEMPLATE(write_dec_checkoncemax_divrem_write1, ty); \ + \ +C4BM_TEMPLATE(write_dec_checkall_singlediv_write2, ty); \ +C4BM_TEMPLATE(write_dec_checkall_singlediv_write1, ty); \ +C4BM_TEMPLATE(write_dec_checkall_divrem_write2, ty); \ +C4BM_TEMPLATE(write_dec_checkall_divrem_write1, ty) + + + +#define C4_INSTANTIATE_WRITE_DEC_BENCHMARKS64(ty) \ + \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_naive_fargies<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_naive_hifirst64fallback32<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write2, ty, impl::digits_dec_naive_lofirst64fallback32<ty>); \ + \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_naive_fargies<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_naive_hifirst64fallback32<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_singlediv_write1, ty, impl::digits_dec_naive_lofirst64fallback32<ty>); \ + \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_naive_fargies<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_naive_hifirst64fallback32<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write2, ty, impl::digits_dec_naive_lofirst64fallback32<ty>); \ + \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_naive_fargies<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_naive_hifirst64fallback32<ty>); \ +C4BM_TEMPLATE(write_dec_checkoncelog_divrem_write1, ty, impl::digits_dec_naive_lofirst64fallback32<ty>) + + +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS(uint8_t, 8); +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS(int8_t, 8); +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS(uint16_t, 16); +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS(int16_t, 16); +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS(uint32_t, 32); +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS(int32_t, 32); +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS64(uint64_t); +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS(uint64_t, 64); +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS64(int64_t); +C4_INSTANTIATE_WRITE_DEC_BENCHMARKS(int64_t, 64); + + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T> struct xtoacase { c4::csubstr str; T val; }; +#define _(num) {c4::csubstr(#num), num##u} +xtoacase<uint8_t> cases_8bit[] = { + _(0), + _(1), + _(9), + _(10), + _(11), + _(98), + _(99), + _(100), + _(101), +}; +xtoacase<uint16_t> cases_16bit[] = { + _(999), + _(1000), + _(1001), + _(9999), + _(10000), + _(10001), +}; +xtoacase<uint32_t> cases_32bit[] = { + _(99999), + _(100000), + _(100001), + _(999999), + _(1000000), + _(1000001), + _(9999999), + _(10000000), + _(10000001), + _(99999999), + _(100000000), + _(100000001), + _(999999999), + _(1000000000), + _(1000000001), +}; +xtoacase<uint64_t> cases_64bit[] = { + _(9999999999), + _(10000000000), + _(10000000001), + _(99999999999), + _(100000000000), + _(100000000001), + _(999999999999), + _(1000000000000), + _(1000000000001), + _(9999999999999), + _(10000000000000), + _(10000000000001), + _(99999999999999), + _(100000000000000), + _(100000000000001), + _(999999999999999), + _(1000000000000000), + _(1000000000000001), + _(9999999999999999), + _(10000000000000000), + _(10000000000000001), + _(99999999999999999), + _(100000000000000000), + _(100000000000000001), + _(999999999999999999), + _(1000000000000000000), + _(1000000000000000001), + _(9223372036854775807), +}; +xtoacase<uint64_t> cases_64bitu[] = { + _(9999999999999999999), + _(10000000000000000000), + _(18446744073709551615), +}; +#undef _ + +bool logtest = true; +bool printok = false; +bool testfail = false; +#define C4_CHECK_(lhs, op, rhs, ...) \ + { \ + if(!((lhs) op (rhs))) \ + { \ + std::cout << __FILE__ << ":" << __LINE__ \ + << ": failed! " << #lhs " " #op " " #rhs "\n" \ + << " " #lhs "=" << (lhs) << "\n" \ + << " " #rhs "=" << (rhs) << "\n" \ + << " " << __VA_ARGS__ << "\n"; \ + testfail = true; \ + } \ + else if(printok) \ + { \ + std::cout << __FILE__ << ":" << __LINE__ \ + << ": ok! " << #lhs " " #op " " #rhs \ + << " " << __VA_ARGS__ << "\n"; \ + } \ + } + +C4_SUPPRESS_WARNING_CLANG("-Wgnu-zero-variadic-macro-arguments") +#define C4_CHECK_LT(lhs, rhs, ...) C4_CHECK_(lhs, <, rhs, ## __VA_ARGS__) +#define C4_CHECK_LE(lhs, rhs, ...) C4_CHECK_(lhs, <=, rhs, ## __VA_ARGS__) +#define C4_CHECK_GT(lhs, rhs, ...) C4_CHECK_(lhs, >, rhs, ## __VA_ARGS__) +#define C4_CHECK_GE(lhs, rhs, ...) C4_CHECK_(lhs, >=, rhs, ## __VA_ARGS__) +#define C4_CHECK_EQ(lhs, rhs, ...) C4_CHECK_(lhs, ==, rhs, ## __VA_ARGS__) +#define C4_CHECK_NE(lhs, rhs, ...) C4_CHECK_(lhs, !=, rhs, ## __VA_ARGS__) + +#define DO_TEST_DIGITS_(ty, fn, num) \ + if(logtest) std::cout << "\ntesting: " #fn "\n"; \ + test_digits##num<ty, fn>(); \ + if(logtest) std::cout << "success: " #fn "\n" + +#define DO_TEST_WRITE_(ty, fn, num) \ + if(logtest) std::cout << "\ntesting: " #fn "\n"; \ + test_write##num<ty>(&fn); \ + if(logtest) std::cout << "success: " #fn "\n" + + +template<class T, class U, class Func> +void test_write(xtoacase<U> c, Func fn) +{ + if(c.val == 0) + return; + C4_STATIC_ASSERT(sizeof(T) >= sizeof(U)); + char buf_[32] = {}; + c4::substr buf = buf_; + C4_CHECK_GT(c.val, 0, c.str << "/" << (uint64_t)c.val); + C4_CHECK_LE((U)c.val, (U)std::numeric_limits<T>::max(), c.str << "/" << (uint64_t)c.val); + T val = (T)c.val; + size_t ret = fn(buf, val); + C4_CHECK_EQ(ret, c.str.len, c.str << "/" << (uint64_t)c.val << ": " << buf.first(ret)); + C4_CHECK_EQ(buf.first(ret), c.str, c.str << "/" << (uint64_t)c.val); +} + +template<class T, msb_func<T> digitsfunc> +void test_digits8() +{ + for(auto c : cases_8bit) + C4_CHECK_EQ(digitsfunc((T)c.val), c.str.len, (uint64_t)c.val); +} +template<class T, class Func> +void test_write8(Func func) +{ + for(auto c : cases_8bit) + test_write<T>(c, func); +} + +template<class T, msb_func<T> digitsfunc> +void test_digits16() +{ + test_digits8<T, digitsfunc>(); + for(auto c : cases_16bit) + C4_CHECK_EQ(digitsfunc((T)c.val), c.str.len, (uint64_t)c.val); +} +template<class T, class Func> +void test_write16(Func func) +{ + test_write8<T>(func); + for(auto c : cases_16bit) + test_write<T>(c, func); +} + +template<class T, msb_func<T> digitsfunc> +void test_digits32() +{ + test_digits8<T, digitsfunc>(); + test_digits16<T, digitsfunc>(); + for(auto c : cases_32bit) + C4_CHECK_EQ(digitsfunc((T)c.val), c.str.len, (uint64_t)c.val); +} +template<class T, class Func> +void test_write32(Func func) +{ + test_write8<T>(func); + test_write16<T>(func); + for(auto c : cases_32bit) + test_write<T>(c, func); +} + +template<class T, msb_func<T> digitsfunc> +void test_digits64() +{ + test_digits8<T, digitsfunc>(); + test_digits16<T, digitsfunc>(); + test_digits32<T, digitsfunc>(); + for(auto c : cases_64bit) + C4_CHECK_EQ(digitsfunc((T)c.val), c.str.len, (uint64_t)c.val); + if(std::is_unsigned<T>::value) + for(auto c : cases_64bitu) + C4_CHECK_EQ(digitsfunc((T)c.val), c.str.len, (uint64_t)c.val << "/" << c.str); +} +template<class T, class Func> +auto test_write64(Func func) + -> typename std::enable_if<std::is_unsigned<T>::value, void>::type +{ + test_write8<T>(func); + test_write16<T>(func); + test_write32<T>(func); + for(auto c : cases_64bit) + test_write<T>(c, func); + for(auto c : cases_64bitu) + test_write<T>(c, func); +} +template<class T, class Func> +auto test_write64(Func func) + -> typename std::enable_if<std::is_signed<T>::value, void>::type +{ + test_write8<T>(func); + test_write16<T>(func); + test_write32<T>(func); + for(auto c : cases_64bit) + test_write<T>(c, func); +} + +#define DO_TEST_DIGITS(ty, num) \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_hifirst<ty>, num); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_lofirst<ty>, num); \ +DO_TEST_DIGITS_(ty, impl::digits_glibc<ty C4_COMMA 10u>, num); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_log10<ty C4_COMMA msb_intrinsic_##num##bit<ty>>, num); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_log10<ty C4_COMMA msb_divconq_##num##bit<ty>>, num); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_log10<ty C4_COMMA msb_loop<ty>>, num) + +#define DO_TEST_DIGITS_64(ty) \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_fargies<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_hifirst64fallback32<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_lofirst64fallback32<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_fargies<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_hifirst64fallback32<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_lofirst64fallback32<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_fargies<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_hifirst64fallback32<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_lofirst64fallback32<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_fargies<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_hifirst64fallback32<ty>, 64); \ +DO_TEST_DIGITS_(ty, impl::digits_dec_naive_lofirst64fallback32<ty>, 64) + + +#define DO_TEST_WRITE(ty, num) \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_naive_hifirst<ty>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_naive_lofirst<ty>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_glibc<ty C4_COMMA 10u>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_intrinsic_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_divconq_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_loop<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_intrinsic_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_divconq_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_loop<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_naive_hifirst<ty>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_naive_lofirst<ty>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_glibc<ty C4_COMMA 10u>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_intrinsic_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_divconq_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_loop<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_intrinsic_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_divconq_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_loop<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_naive_hifirst<ty>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_naive_lofirst<ty>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_glibc<ty C4_COMMA 10u>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_intrinsic_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_divconq_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_loop<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_intrinsic_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_divconq_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_loop<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_naive_hifirst<ty>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_naive_lofirst<ty>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_glibc<ty C4_COMMA 10u>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_intrinsic_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_divconq_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_log10_nocheck<ty C4_COMMA msb_loop<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_intrinsic_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_divconq_##num##bit<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_log10<ty C4_COMMA msb_loop<ty>>>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncemax_singlediv_write2<ty>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncemax_singlediv_write1<ty>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncemax_divrem_write2<ty>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncemax_divrem_write1<ty>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkall_singlediv_write2<ty>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkall_singlediv_write1<ty>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkall_divrem_write2<ty>, num);\ +DO_TEST_WRITE_(ty, impl::write_dec_checkall_divrem_write1<ty>, num) + + +#define DO_TEST_WRITE_64(ty) \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_naive_fargies<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_naive_hifirst64fallback32<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write2<ty C4_COMMA impl::digits_dec_naive_lofirst64fallback32<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_naive_fargies<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_naive_hifirst64fallback32<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_singlediv_write1<ty C4_COMMA impl::digits_dec_naive_lofirst64fallback32<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_naive_fargies<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_naive_hifirst64fallback32<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write2<ty C4_COMMA impl::digits_dec_naive_lofirst64fallback32<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_naive_fargies<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_naive_hifirst64fallback32<ty>>, 64); \ +DO_TEST_WRITE_(ty, impl::write_dec_checkoncelog_divrem_write1<ty C4_COMMA impl::digits_dec_naive_lofirst64fallback32<ty>>, 64) + + +void do_test() +{ + DO_TEST_DIGITS(uint8_t, 8); + DO_TEST_DIGITS(int8_t, 8); + DO_TEST_DIGITS(uint16_t, 16); + DO_TEST_DIGITS(int16_t, 16); + DO_TEST_DIGITS(uint32_t, 32); + DO_TEST_DIGITS(int32_t, 32); + DO_TEST_DIGITS(uint64_t, 64); + DO_TEST_DIGITS(int64_t, 64); + DO_TEST_DIGITS_64(uint64_t); + DO_TEST_DIGITS_64(int64_t); + printf("\n"); + DO_TEST_WRITE(uint8_t, 8); + DO_TEST_WRITE(int8_t, 8); + DO_TEST_WRITE(uint16_t, 16); + DO_TEST_WRITE(int16_t, 16); + DO_TEST_WRITE(uint32_t, 32); + DO_TEST_WRITE(int32_t, 32); + DO_TEST_WRITE(uint64_t, 64); + DO_TEST_WRITE(int64_t, 64); + DO_TEST_WRITE_64(uint64_t); + DO_TEST_WRITE_64(int64_t); + printf("\n"); + C4_CHECK(!testfail); +} + + +int main(int argc, char *argv[]) +{ + //do_test(); + bm::Initialize(&argc, argv); + bm::RunSpecifiedBenchmarks(); + return 0; +} diff --git a/thirdparty/ryml/ext/c4core/bm/float/measure.py b/thirdparty/ryml/ext/c4core/bm/float/measure.py new file mode 100644 index 000000000..253d03a45 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/float/measure.py @@ -0,0 +1,65 @@ +import time +import argparse +import os +import subprocess +import shlex +from ruamel import yaml + + +def runcmd(cmd, *cmd_args, **subprocess_args): + cmd = shlex.split(cmd) + list(cmd_args) + #print(" ".join([f"'{a}'" for a in cmd]), flush=True) + proc = subprocess.run(cmd, **subprocess_args) + return proc + + +def getoutput(cmd, *cmd_args, **subprocess_args): + proc = runcmd(cmd, *cmd_args, **subprocess_args, check=True, + stdout=subprocess.PIPE) + return proc.stdout.decode("utf8") + + +def start_build(args): + ts = time.time() + with open(args.out, "w") as f: + f.write(str(ts)) + + +def finish_build(args): + ts = time.time() + with open(args.out, "r") as f: + start = float(f.read()) + duration = ts - start + results = { + 'compile': f"{duration:.3f}s", + 'file_size': f"{os.path.getsize(args.exe)}B" + } + s = yaml.dump({args.target: results}) + print(s, flush=True, end="") + ## too much output: + #if args.unix: + # # https://stackoverflow.com/questions/35485 + # results['size'] = getoutput('size', args.exe) + # #results['symbols'] = getoutput('nm -t d -l -S --size-sort', args.exe) + s = yaml.dump({args.target: results}) + with open(args.out, "w") as f: + f.write(s) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers() + # + sp = subparsers.add_parser("start") + sp.set_defaults(func=start_build) + sp.add_argument('target', type=str, help='the target name') + # + sp = subparsers.add_parser("finish") + sp.set_defaults(func=finish_build) + sp.add_argument('target', type=str, help='the target name') + sp.add_argument('exe', type=str, help='the executable file') + sp.add_argument('-u', '--unix', action="store_true", help='use unix style size reporters') + # + args = parser.parse_args() + args.out = f"{args.target}.dat" + args.func(args) diff --git a/thirdparty/ryml/ext/c4core/bm/float/read.cpp b/thirdparty/ryml/ext/c4core/bm/float/read.cpp new file mode 100644 index 000000000..5401ff1bb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/float/read.cpp @@ -0,0 +1,170 @@ +#include <cstdio> + + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4430) +# pragma warning(disable : 4305) +# pragma warning(disable : 4309) +# pragma warning(disable : 4838) +# pragma warning(disable : 4996) +#elif defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-conversion" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + + +#if C4FLOAT_STD_ATOF +#include <cstdlib> +double doit(const char *s) { return atof(s); } +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_SSCANF_F +float doit(const char *s) { float val; sscanf(s, "%f", &val); return val; } +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_SSCANF_D +double doit(const char *s) { double val; sscanf(s, "%lf", &val); return val; } +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_IOSTREAM_F +#include <sstream> +float doit(const char *s) { std::stringstream ss; ss << s; float val; ss >> val; return val; } +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_IOSTREAM_D +#include <sstream> +double doit(const char *s) { std::stringstream ss; ss << s; double val; ss >> val; return val; } +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_IOSTREAM_D +#include <sstream> +double doit(const char *s) { std::stringstream ss; ss << s; double val; ss >> val; return val; } +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_FP_F_LIMITED +#include <jkj/fp/from_chars/from_chars.h> +float doit(const char *s) +{ + auto result = jkj::fp::from_chars_limited<float>(s, s+strlen(s)); + return result.to_float(); +} +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_FP_D_LIMITED +#include <jkj/fp/from_chars/from_chars.h> +double doit(const char *s) +{ + auto result = jkj::fp::from_chars_limited<double>(s, s+strlen(s)); + return result.to_float(); +} +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_FP_F_UNLIMITED +#include <jkj/fp/from_chars/from_chars.h> +float doit(const char *s) +{ + auto result = jkj::fp::from_chars_unlimited<float>(s, s+strlen(s)); + return result.to_float(); +} +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_FP_D_UNLIMITED +#include <jkj/fp/from_chars/from_chars.h> +double doit(const char *s) +{ + auto result = jkj::fp::from_chars_unlimited<double>(s, s+strlen(s)); + return result.to_float(); +} +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_FASTFLOAT_F +#include <c4/ext/fast_float.hpp> +#include <cstring> +float doit(const char *s) +{ + float result; + fast_float::from_chars(s, s+strlen(s), result); + return result; +} +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_FASTFLOAT_D +#include <c4/ext/fast_float.hpp> +#include <cstring> +double doit(const char *s) +{ + double result; + fast_float::from_chars(s, s+strlen(s), result); + return result; +} +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_STD_FROM_CHARS_F +#include <charconv> +#include <cstring> +float doit(const char *s) +{ + float result; + std::from_chars(s, s+strlen(s), result); + return result; +} +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_STD_FROM_CHARS_D +#include <charconv> +#include <cstring> +double doit(const char *s) +{ + double result; + std::from_chars(s, s+strlen(s), result); + return result; +} +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_RYU_F +#include <ryu/ryu_parse.h> +float doit(const char *s) +{ + float result; + s2f(s, &result); + return result; +} +#define C4_TO_REAL(s) doit(s) + +#elif C4FLOAT_RYU_D +#include <ryu/ryu_parse.h> +double doit(const char *s) +{ + double result; + s2d(s, &result); + return result; +} +#define C4_TO_REAL(s) doit(s) + +#else +#define C4_TO_REAL(s) 0 +#endif + +int main() +{ + #define BUFSIZE 128 + char buf[BUFSIZE]; + while(fgets(buf, BUFSIZE, stdin)) + { + fputs(buf, stdout); + (void) C4_TO_REAL(buf); + } +} + + +#ifdef _MSC_VER +# pragma warning(pop) +#elif defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/thirdparty/ryml/ext/c4core/bm/ryu.cmake b/thirdparty/ryml/ext/c4core/bm/ryu.cmake new file mode 100644 index 000000000..93079a489 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/bm/ryu.cmake @@ -0,0 +1,37 @@ +# ryu does not have a cmakelists + +if(C4CORE_BM_USE_RYU) + enable_language(C) + + c4_download_remote_proj(ryu RYU_DIR + GIT_REPOSITORY https://github.com/ulfjack/ryu + GIT_TAG master GIT_SHALLOW ON) + set(RYU_HDR + ${RYU_DIR}/ryu/common.h + ${RYU_DIR}/ryu/d2fixed_full_table.h + ${RYU_DIR}/ryu/d2s_full_table.h + ${RYU_DIR}/ryu/d2s_intrinsics.h + ${RYU_DIR}/ryu/d2s_small_table.h + ${RYU_DIR}/ryu/digit_table.h + ${RYU_DIR}/ryu/f2s_full_table.h + ${RYU_DIR}/ryu/f2s_intrinsics.h + ${RYU_DIR}/ryu/ryu.h + ${RYU_DIR}/ryu/ryu_parse.h + ) + set(RYU_SRC + ${RYU_DIR}/ryu/d2fixed.c + ${RYU_DIR}/ryu/d2s.c + ${RYU_DIR}/ryu/f2s.c + ${RYU_DIR}/ryu/s2d.c + ${RYU_DIR}/ryu/s2f.c + ) + add_library(ryu_c4 ${RYU_SRC} ${RYU_HDR}) + target_include_directories(ryu_c4 PUBLIC $<BUILD_INTERFACE:${RYU_DIR}>) + set_target_properties(ryu_c4 PROPERTIES LINKER_LANGUAGE CXX) + if(CMAKE_CXX_COMPILER_ID STREQUAL GNU) + target_compile_options(ryu_c4 PRIVATE -Wno-sign-conversion) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL GNU) + target_compile_options(ryu_c4 -Wno-deprecated) + endif() + _c4_set_target_folder(ryu_c4 ext) +endif() diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.0.md b/thirdparty/ryml/ext/c4core/changelog/0.1.0.md new file mode 100644 index 000000000..7e9e8a67b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.0.md @@ -0,0 +1,3 @@ +# 0.1.0 + +First release. diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.1.md b/thirdparty/ryml/ext/c4core/changelog/0.1.1.md new file mode 100644 index 000000000..934c65f30 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.1.md @@ -0,0 +1,5 @@ +# 0.1.1 + +- Fix parsing of hexadecimal floats ([2d5c3f0](https://github.com/biojppm/c4core/commits/2d5c3f0)) +- Fix `csubstr::reverse_sub()` ([902c5b9](https://github.com/biojppm/c4core/commits/902c5b9)) +- Fix [#35](https://github.com/biojppm/c4core/issues/35): add SO_VERSION diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.10.md b/thirdparty/ryml/ext/c4core/changelog/0.1.10.md new file mode 100644 index 000000000..ce95aaee1 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.10.md @@ -0,0 +1,106 @@ +### Changes + +Improved the performance of `c4/charconv.hpp` functions ([PR#77](https://github.com/biojppm/c4core/pull/77)): + - Added `digits_dec/hex/oct/bin()`. + - Optimized `write_dec/hex/oct/bin()`: + - these functions now return immediately without entering the loop if the output buffer is smaller than respectively `digits_dec/hex/oct/bin()`. This enables both: + - writing every character in its final position without having to revert the string at the end + - the need to check the buffer size on appending every character. + - `write_dec()` now writes two digits at once, thus halving the number of integer divisions. + - Added `write_dec/hex/oct/bin_unchecked()`, which receive precomputed `digits_dec/hex/oct/bin()`, thus speeding up the radix `itoa()/utoa()` overloads. + - Added `xtoa()` radix+digits overloads: + - `size_t xtoa(substr s, T v, T radix)` + - `size_t xtoa(substr s, T v, T radix, size_t num_digits)` + - `read_dec/hex/oct/bin()`: these functions no longer allow an empty input buffer. + - Use intrinsic functions `__builtin_clz()` (gcc) / `_BitScanReverse()` (msvc) in `c4::msb()` and `__builtin_ctz()` (gcc) / `_BitScanForward()` (msvc) in `c4::lsb()` when they are available. `msb()` is used by `digits_hex()/digits_bin()`. + - Refactored the charconv tests to improve consistency and thoroughness. + - Improved the charconv benchmarks to ensure full consistency across benchmarks. + - Special thanks and kudos to @fargies for being attentive and pinpointing several issues throughout the PR! + - Finding the best approach involved [writing a R&D benchmark for the several algorithm components](https://github.com/biojppm/c4core/tree/master/bm/bm_xtoa.cpp). This benchmark is disabled by default, and can be enabled with the flag `C4CORE_BM_XTOA_RND`. + - With the changes from this PR, the [charconv benchmark](https://github.com/biojppm/c4core/tree/master/bm_charconv.cpp) results show that on Linux/g++11.2, with integral types: + - `c4::to_chars()` can be expected to be roughly... + - ~40% to 2x faster than `std::to_chars()` + - ~10x-30x faster than `sprintf()` + - ~50x-100x faster than a naive `stringstream::operator<<()` followed by `stringstream::str()` + - `c4::from_chars()` can be expected to be roughly... + - ~10%-30% faster than `std::from_chars()` + - ~10x faster than `scanf()` + - ~30x-50x faster than a naive `stringstream::str()` followed by `stringstream::operator>>()` + - Here are the results from the run: + | Write throughput | | Read throughput | | + |:-------------------------|--------:|:-------------------------|---------:| + | **write `uint8_t`** | **MB/s**| **read `uint8_t`** | **MB/s**| + | `c4::to_chars<u8>` | 526.86 | `c4::from_chars<u8>` | 163.06 | + | `std::to_chars<u8>` | 379.03 | `std::from_chars<u8>` | 154.85 | + | `std::sprintf<u8>` | 20.49 | `std::scanf<u8>` | 15.75 | + | `std::stringstream<u8>` | 3.82 | `std::stringstream<u8>` | 3.83 | + | **write `int8_t`** | **MB/s**| **read `int8_t`** | **MB/s**| + | `c4::to_chars<i8>` | 599.98 | `c4::from_chars<i8>` | 184.20 | + | `std::to_chars<i8>` | 246.32 | `std::from_chars<i8>` | 156.40 | + | `std::sprintf<i8>` | 19.15 | `std::scanf<i8>` | 16.44 | + | `std::stringstream<i8>` | 3.83 | `std::stringstream<i8>` | 3.89 | + | **write `uint16_t`** | **MB/s**| **read `uint16_t`** | **MB/s**| + | `c4::to_chars<u16>` | 486.40 | `c4::from_chars<u16>` | 349.48 | + | `std::to_chars<u16>` | 454.24 | `std::from_chars<u16>` | 319.13 | + | `std::sprintf<u16>` | 38.74 | `std::scanf<u16>` | 28.12 | + | `std::stringstream<u16>` | 7.08 | `std::stringstream<u16>`| 6.73 | + | **write `int16_t`** | **MB/s**| **read `int16_t`** | **MB/s**| + | `c4::to_chars<i16>` | 507.44 | `c4::from_chars<i16>` | 282.95 | + | `std::to_chars<i16>` | 297.49 | `std::from_chars<i16>` | 186.18 | + | `std::sprintf<i16>` | 39.03 | `std::scanf<i16>` | 28.45 | + | `std::stringstream<i16>` | 6.98 | `std::stringstream<i16>`| 6.49 | + | **write `uint32_t`** | **MB/s**| **read `uint32_t`** | **MB/s**| + | `c4::to_chars<u32>` | 730.12 | `c4::from_chars<u32>` | 463.95 | + | `std::to_chars<u32>` | 514.76 | `std::from_chars<u32>` | 329.42 | + | `std::sprintf<u32>` | 71.19 | `std::scanf<u32>` | 44.97 | + | `std::stringstream<u32>` | 14.05 | `std::stringstream<u32>`| 12.57 | + | **write `int32_t`** | **MB/s**| **read `int32_t`** | **MB/s**| + | `c4::to_chars<i32>` | 618.76 | `c4::from_chars<i32>` | 345.53 | + | `std::to_chars<i32>` | 394.72 | `std::from_chars<i32>` | 224.46 | + | `std::sprintf<i32>` | 71.14 | `std::scanf<i32>` | 43.49 | + | `std::stringstream<i32>` | 13.91 | `std::stringstream<i32>`| 12.03 | + | **write `uint64_t`** | **MB/s**| **read `uint64_t`** | **MB/s**| + | `c4::to_chars<u64>` | 1118.87 | `c4::from_chars<u64>` | 928.49 | + | `std::to_chars<u64>` | 886.58 | `std::from_chars<u64>` | 759.03 | + | `std::sprintf<u64>` | 140.96 | `std::scanf<u64>` | 91.60 | + | `std::stringstream<u64>` | 28.01 | `std::stringstream<u64>`| 25.00 | + | **write `int64_t`** | **MB/s**| **read `int64_t`** | **MB/s**| + | `c4::to_chars<i64>` | 1198.78 | `c4::from_chars<i64>` | 713.76 | + | `std::to_chars<i64>` | 882.17 | `std::from_chars<i64>` | 646.18 | + | `std::sprintf<i64>` | 138.79 | `std::scanf<i64>` | 90.07 | + | `std::stringstream<i64>` | 27.62 | `std::stringstream<i64>`| 25.12 | + +If you feel suspicious about these bold claims, you can browse through [c4core's CI benchmark results](https://github.com/biojppm/c4core/actions/workflows/benchmarks.yml) which will hopefully give these more substance. + + +### New features + +- Added `bool c4::overflows<T>(csubstr s)` for detecting whether a string overflows a given integral type. See [PR#78](https://github.com/biojppm/c4core/pull/78). + - Also, added `c4::fmt::overflow_checked()` (and the corresponding `from_chars()` overload) to enable a check for overflow before parsing from string: + ```c++ + c4::from_chars(str, &val); // no overflow check + c4::from_chars(str, c4::fmt::overflow_checked(val)); // enable overflow check + // as an example, the implementation looks like: + template<class T> + bool c4::from_chars(c4::csubstr str, c4::fmt::overflow_checked<T> oc) + { + if(overflows<T>(str)) + return false; + return c4::from_chars(str, oc.val); + } + ``` + +### Fixes + +- Fix missing endianess macro on windows arm/arm64 compilations [PR #76](https://github.com/biojppm/c4core/pull/76) +- Add missing `#define` for the include guard of the amalgamated header (see [rapidyaml#246](https://github.com/biojppm/rapidyaml/issues/246)). +- Fix CPU detection with ARMEL [PR #86](https://github.com/biojppm/c4core/pull/86). +- Fix GCC version detection [PR #87](https://github.com/biojppm/c4core/pull/87). +- Fix [cmake#8](https://github.com/biojppm/cmake/issues/8): `SOVERSION` missing from shared libraries. +- Update fastfloat to 3.5.1. + +### Thanks + +- @fargies +- @daichifukui +- @janisozaur diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.11.md b/thirdparty/ryml/ext/c4core/changelog/0.1.11.md new file mode 100644 index 000000000..7c5ce8600 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.11.md @@ -0,0 +1,59 @@ + +### Breaking changes + +- `csubstr::operator==(std::nullptr_t)` now strictly checks if the pointer is null and no longer looks at the length ([rapidyaml#264](https://github.com/biojppm/rapidyaml/pull/264)): + ```diff + -bool csubstr::operator== (std::nullptr_t) const noexcept { return str == nullptr || len == 0; } + -bool csubstr::operator!= (std::nullptr_t) const noexcept { return str != nullptr || len == 0; } + +bool csubstr::operator== (std::nullptr_t) const noexcept { return str == nullptr; } + +bool csubstr::operator!= (std::nullptr_t) const noexcept { return str != nullptr; } + ``` +- `to_substr(std::string &s)` and `to_csubstr(std::string const& s)` now point at the first element when the string is empty ([rapidyaml#264](https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1264421024)): + ```diff + - return c4::substr(!s.empty() ? &s[0] : nullptr, s.size()); + + return c4::substr(&s[0], s.size()); + ``` + This is OK because an empty `std::string` is guaranteed to have storage, so calling `s[0]` is safe. + + +### New features + +- `charconv.hpp`: added `xtoa()` floating-point overloads accepting precision and format ([PR#88](https://github.com/biojppm/c4core/pull/88)): + ```c++ + size_t xtoa(substr s, float v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept; + size_t xtoa(substr s, double v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept; + ``` +- `memory_util.hpp`: added `ipow()` overloads for computing powers with integral exponents ([PR#88](https://github.com/biojppm/c4core/pull/88)). +- Add `C4_NO_DEBUG_BREAK` preprocessor check to disable calls to `c4::debug_break()` (see [rapidyaml#326](https://github.com/biojppm/rapidyaml/issues/326)) + - The cmake project conditionally enables this macro if the cmake option `C4CORE_NO_DEBUG_BREAK` is set to `ON`. + + +### Fixes + +- `substr`, `to_chars()`, charconv: ensure `memcpy()` is not called when the length is zero. Doing this is UB and enabled the optimizer to wreak havoc in the branches of calling code. See comments at [rapidyaml#264](https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637) for an example and fix. See [Raymond Chen's blog](https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=633) for an explanation. +- `atof()` and `atod()` ([PR#88](https://github.com/biojppm/c4core/pull/88)): + - Always use the fastest implementation available: `std::from_chars()` if available (C++17 or higher standard, with later compilers), `fast_float::from_chars()` otherwise. On Visual Studio, `fast_float::from_chars()` is preferred over `std::from_chars()`. + - If `std::from_chars()` is not available and `C4CORE_NO_FAST_FLOAT` is defined, then the fallback is based on `sscanf()`. + - Ensure hexadecimal floats are accepted. The current fast_float implementation does not accept hexadecimal floats, so an hexfloat scanner was added. +- Likewise for `ftoa()` and `dtoa()`. Prefer the fastest implementation available: `std::to_chars()`->`snprintf()`. + - Change the `FTOA_*` enum values and type to save a function call when converting format. From now on, only the symbols of this enum can be relied on; the values or type will change depending on the selected implementation (`std::to_chars()` or `snprintf()`) ([PR#91](https://github.com/biojppm/c4core/pull/91)). +- Fix [#84](https://github.com/biojppm/c4core/issues/84): `csubstr::compare(char)`: refactor to avoid false-positive warning from VS2022. +- `csubstr` methods: add `noexcept` and annotations `C4_PURE` and `C4_ALWAYS_INLINE` +- `csubstr`: add `C4_RESTRICT` to incoming string on `csubstr::compare()` +- `csubstr::first_real_span()` ([PR#89](https://github.com/biojppm/c4core/pull/89)): + - Refactor to fix number matching rules. Now fully valid for floating point numbers in decimal (eg `0.123/1.23e+01`), hexadecimal (eg `0x123.abc/0x1.23abcp+01`), binary (eg `0b101.10/0b1.0110p+01`) and octal format (eg `0o701.10/0o7.0110p+01`) , with or without exponent or power, in lower or upper case. + - Also, make the number parsing stateful to fix cases where repeated characters occur, (like e.g. `0.1.0` or `1.23e+e10`) which are no longer reported as numbers (see [biojppm/rapidyaml#291](https://github.com/biojppm/rapidyaml/issues/291)). +- `csubstr::first_int_span()`, `csubstr::first_uint_span()`: fix edge cases like e.g. `0xzz` which were wrongly reported as numbers. +- Add fully qualified ARM detection macros: + - `__ARM_ARCH_7EM__` ([PR#90](https://github.com/biojppm/c4core/pull/90)). + - `__ARM_ARCH_6KZ__` ([PR#93](https://github.com/biojppm/c4core/pull/93)). + - `__ARM_ARCH_8A__` ([#94](https://github.com/biojppm/c4core/issues/94)). +- Improve linux and unix platform detection: detect both `__linux` and `__linux__` ([PR#92](https://github.com/biojppm/c4core/pull/92)). + + +### Thanks + +- @mlondono74 +- @musicinmybrain +- @pkubaj +- @Gei0r diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.2.md b/thirdparty/ryml/ext/c4core/changelog/0.1.2.md new file mode 100644 index 000000000..d9c2e9c6c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.2.md @@ -0,0 +1,4 @@ +- Fix error macros (ie `C4_ERROR()`, `C4_CHECK()`, `C4_ASSERT()`, etc) such that they are a single statement +- `is_debugger_attached()`: add MacOSX version +- Add support for Visual Studio 2022 +- Ensure `C4_LITTLE_ENDIAN` is always defined, even with mixed endianness diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.3.md b/thirdparty/ryml/ext/c4core/changelog/0.1.3.md new file mode 100644 index 000000000..f98b88f75 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.3.md @@ -0,0 +1 @@ +- Update fast_float to [3.2.1](https://github.com/fastfloat/fast_float/releases/tag/v3.2.0) diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.4.md b/thirdparty/ryml/ext/c4core/changelog/0.1.4.md new file mode 100644 index 000000000..b8e88203e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.4.md @@ -0,0 +1,6 @@ +- [PR #38](https://github.com/biojppm/c4core/pull/38): add s390x architecture feature macros. +- Fix compiler warnings after update of fast_float to [3.2.1](https://github.com/fastfloat/fast_float/releases/tag/v3.2.0). + +### Thanks + +@musicinmybrain diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.5.md b/thirdparty/ryml/ext/c4core/changelog/0.1.5.md new file mode 100644 index 000000000..e4884472e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.5.md @@ -0,0 +1,2 @@ +- Add support for aarch64, s390x, ppc64le CPU architectures +- Update debugbreak header (added support for the above architectures) diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.6.md b/thirdparty/ryml/ext/c4core/changelog/0.1.6.md new file mode 100644 index 000000000..296de1398 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.6.md @@ -0,0 +1,2 @@ +- Fix wrong version names in version 0.1.5 (was saying 0.1.4, should be 0.1.5) + diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.7.md b/thirdparty/ryml/ext/c4core/changelog/0.1.7.md new file mode 100644 index 000000000..2b053a069 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.7.md @@ -0,0 +1,5 @@ +- Fix build with C4CORE_NO_FAST_FLOAT ([#42](https://github.com/biojppm/c4core/pull/42)). +- Fix clang warning in AIX/xlclang ([#44](https://github.com/biojppm/c4core/pull/44)). + +### Thanks +--- @mbs-c diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.8.md b/thirdparty/ryml/ext/c4core/changelog/0.1.8.md new file mode 100644 index 000000000..db77aaf97 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.8.md @@ -0,0 +1,45 @@ + +### New features + +- Add amalgamation into a single header file ([PR #48](https://github.com/biojppm/c4core/pull/48)): + - The amalgamated header will be available together with the deliverables from each release. + - To generate the amalgamated header: + ``` + $ python tools/amalgamate.py c4core_all.hpp + ``` + - To use the amalgamated header: + - Include at will in any header of your project. + - In one - and only one - of your project source files, `#define C4CORE_SINGLE_HDR_DEFINE_NOW` and then `#include <c4core_all.hpp>`. This will enable the function and class definitions in the header file. For example, here's a sample program: + ```c++ + #include <iostream> + #define C4CORE_SINGLE_HDR_DEFINE_NOW // do this before the include + #include <c4core_all.hpp> + int main() + { + for(c4::csubstr s : c4::csubstr("a/b/c/d").split('/')) + std::cout << s << "\n"; + } + ``` +- Add `csubstr::is_unsigned_integer()` and `csubstr::is_real()` ([PR #49](https://github.com/biojppm/c4core/pull/49)). +- CMake: add alias target c4core::c4core, guaranteeing that the same code can be used with `add_subdirectory()` and `find_package()`. (see [rapidyaml #173](https://github.com/biojppm/rapidyaml/issues/173)) +- Add support for compilation with emscripten (WebAssembly+javascript) ([PR #52](https://github.com/biojppm/c4core/pull/52)). + + +### Fixes + +- Fix edge cases with empty strings in `span::first()`, `span::last()` and `span::range()` ([PR #49](https://github.com/biojppm/c4core/pull/49)). +- Accept octal numbers in `substr::first_real_span()` and `substr::is_real()` ([PR #49](https://github.com/biojppm/c4core/pull/49)). +- `substr`: fix coverage misses in number query methods ([PR #49](https://github.com/biojppm/c4core/pull/49)). +- Use single-header version of fast_float ([PR #49](https://github.com/biojppm/c4core/pull/47)). +- Suppress warnings triggered from fast_float in clang (`-Wfortify-source`) ([PR #49](https://github.com/biojppm/c4core/pull/47)). +- Add missing `inline` in [src/c4/ext/rng/rng.hpp](src/c4/ext/rng/rng.hpp) ([PR #49](https://github.com/biojppm/c4core/pull/47)). +- Fix compilation of [src/c4/ext/rng/inplace_function.h](src/c4/ext/inplace_function.h) in C++11 ([PR #49](https://github.com/biojppm/c4core/pull/47)). +- Change order of headers, notably in `windows_push.hpp` ([PR #47](https://github.com/biojppm/c4core/pull/47)). +- In `c4/charconv.hpp`: do not use C4_ASSERT in `to_c_fmt()`, which is `constexpr`. +- Fix [#53](https://github.com/biojppm/c4core/issues/53): cmake install targets were missing call to `export()` ([PR #55](https://github.com/biojppm/c4core/pull/55)). +- Fix linking of subprojects with libc++: flags should be forwarded through `CMAKE_***_FLAGS` instead of being set explicitly per-target ([PR #54](https://github.com/biojppm/c4core/pull/54)). + + +### Thanks + +- @cschreib diff --git a/thirdparty/ryml/ext/c4core/changelog/0.1.9.md b/thirdparty/ryml/ext/c4core/changelog/0.1.9.md new file mode 100644 index 000000000..1ac5aa842 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/0.1.9.md @@ -0,0 +1,31 @@ +### Breaking changes + +- fix [#63](https://github.com/biojppm/c4core/issues/63): remove `c4/time.hpp` and `c4/time.cpp` which prevented compilation in bare-metal mode ([PR #64](https://github.com/biojppm/c4core/issues/64)). + +### New features + +- Added decoding of UTF codepoints: `c4::decode_code_point()` ([PR #65](https://github.com/biojppm/c4core/issues/65)). +- Experimental feature: add formatted-dumping facilities: using semantics like `c4::cat()`, `c4::catsep()` and `c4::format()`, where the subject is not a string buffer but a dump callback accepting strings. This still requires a string buffer for serialization of non-string types, but the buffer's required size is now limited to the max serialized size of non-string arguments, in contrast to the requirement in `c4::cat()` et al which is the total serialized size of every argument. This enables very efficient and generic printf-like semantics with reuse of a single small buffer, and allows direct-printing to terminal or file ([PR #67](https://github.com/biojppm/c4core/issues/67)). This feature is still experimental and a minor amount of changes to the API is possible. +- Added macro `C4_IF_CONSTEXPR` resolving to `if constexpr (...)` if the c++ standard is at least c++17. +- `csubstr`: add `count(csubstr)` overload. +- Add support for RISC-V architectures ([PR #69](https://github.com/biojppm/c4core/issues/69)). +- Add support for bare-metal compilation ([PR #64](https://github.com/biojppm/c4core/issues/64)). +- gcc >= 4.8 support using polyfills for missing templates and features ([PR #74](https://github.com/biojppm/c4core/pull/74) and [PR #68](https://github.com/biojppm/c4core/pull/68)). + +### Fixes + +- `csubstr::operator==(std::nullptr_t)` now returns true if either `.str==nullptr` or `.len==0`. +- Fix: `bool operator==(const char (&s)[N], csubstr)` and `operator==(const char (&s)[N], substr)`. The template declaration for these functions had an extra `const` which prevented these functions to participate in overload resolution, which in some cases resulted in calls resolving to `operator==(std::string const&, csubstr)` if that header was visible ([PR #64](https://github.com/biojppm/c4core/issues/64)). +- Fix `csubstr::last_not_of()`: optional positional parameter was ignored [PR #62](https://github.com/biojppm/c4core/pull/62). +- `atof()`, `atod()`, `atox()`, `substr::is_real()`, `substr::first_real_span()`: accept `infinity`, `inf` and `nan` as valid reals [PR #60](https://github.com/biojppm/c4core/pull/60). +- Add missing export symbols [PR #56](https://github.com/biojppm/c4core/pull/56), [PR #57](https://github.com/biojppm/c4core/pull/57). +- `c4/substr_fwd.hpp`: fix compilation failure in Xcode 12 and earlier, where the forward declaration for `std::allocator` is inside the `inline namespace __1`, unlike later versions [PR #61](https://github.com/biojppm/c4core/pull/61), reported in [rapidyaml#185](https://github.com/biojppm/rapidyaml/issues/185). +- `c4/error.hpp`: fix compilation failure in debug mode in Xcode 12 and earlier: `__clang_major__` does not mean the same as in the common clang, and as a result the warning `-Wgnu-inline-cpp-without-extern` does not exist there. + + +### Thanks + +- @danngreen +- @Xeonacid +- @aviktorov +- @fargies diff --git a/thirdparty/ryml/ext/c4core/changelog/current.md b/thirdparty/ryml/ext/c4core/changelog/current.md new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/changelog/current.md diff --git a/thirdparty/ryml/ext/c4core/cmake/.gitignore b/thirdparty/ryml/ext/c4core/cmake/.gitignore new file mode 100644 index 000000000..ed8ebf583 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/.gitignore @@ -0,0 +1 @@ +__pycache__
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/cmake/ConfigurationTypes.cmake b/thirdparty/ryml/ext/c4core/cmake/ConfigurationTypes.cmake new file mode 100644 index 000000000..45395ad1b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/ConfigurationTypes.cmake @@ -0,0 +1,120 @@ + + +# this function works both with multiconfig and single-config generators. +function(set_default_build_type which) + # CMAKE_CONFIGURATION_TYPES is available only for multiconfig generators. + # so set the build type only if CMAKE_CONFIGURATION_TYPES does not exist. + if(NOT CMAKE_CONFIGURATION_TYPES) # not a multiconfig generator? + if(NOT CMAKE_BUILD_TYPE) + if(NOT which) + set(which RelWithDebInfo) + endif() + message("Defaulting to ${which} build.") + set(CMAKE_BUILD_TYPE ${which} CACHE STRING "") + endif() + endif() +endfunction() + + +# https://stackoverflow.com/questions/31546278/where-to-set-cmake-configuration-types-in-a-project-with-subprojects +function(setup_configuration_types) + set(options0arg + ) + set(options1arg + DEFAULT + ) + set(optionsnarg + TYPES + ) + cmake_parse_arguments("" "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN}) + + if(NOT TYPES) + set(TYPES Release Debug RelWithDebInfo MinSizeRel) + endif() + + # make it safe to call repeatedly + if(NOT _setup_configuration_types_done) + set(_setup_configuration_types_done 1 CACHE INTERNAL "") + + # No reason to set CMAKE_CONFIGURATION_TYPES if it's not a multiconfig generator + # Also no reason mess with CMAKE_BUILD_TYPE if it's a multiconfig generator. + + if(CMAKE_CONFIGURATION_TYPES) # multiconfig generator? + set(CMAKE_CONFIGURATION_TYPES "${TYPES}" CACHE STRING "") + else() # single-config generator + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build") + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${TYPES}") + # set the valid options for cmake-gui drop-down list + endif() + endif() +endfunction() + + +# https://stackoverflow.com/questions/31546278/where-to-set-cmake-configuration-types-in-a-project-with-subprojects +function(add_configuration_type name) + set(flag_vars + C_FLAGS + CXX_FLAGS + SHARED_LINKER_FLAGS + STATIC_LINKER_FLAGS + MODULE_LINKER_FLAGS + EXE_LINKER_FLAGS + RC_FLAGS + ) + + set(options0arg + PREPEND # when defaulting to a config, prepend to it instead of appending to it + SET_MAIN_FLAGS # eg, set CMAKE_CXX_FLAGS from CMAKE_CXX_FLAGS_${name} + ) + set(options1arg + DEFAULT_FROM # take the initial value of the flags from this config + ) + set(optionsnarg + C_FLAGS + CXX_FLAGS + SHARED_LINKER_FLAGS + STATIC_LINKER_FLAGS + MODULE_LINKER_FLAGS + EXE_LINKER_FLAGS + RC_FLAGS + ) + cmake_parse_arguments(_act "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN}) + + string(TOUPPER ${name} UNAME) + + # make it safe to call repeatedly + if(NOT _add_configuration_type_${name}) + set(_add_configuration_type_${name} 1 CACHE INTERNAL "") + + setup_configuration_types() + + if(CMAKE_CONFIGURATION_TYPES) # multiconfig generator? + set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES};${name}" CACHE STRING "" FORCE) + else() # single-config generator + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${CMAKE_BUILD_TYPES};${name}" FORCE) + # set the valid options for cmake-gui drop-down list + endif() + + # now set up the configuration + message(STATUS "config: CMAKE_${f}_${UNAME} --- ${val}") + foreach(f ${flag_vars}) + set(val ${_act_${f}}) + message(STATUS "config: ${name}: ${f} --- ${val}") + if(_act_DEFAULT_FROM) + if(_act_PREPEND) + set(val "${val} ${CMAKE_${f}_${_act_DEFAULT_FROM}}") + else() + set(val "${CMAKE_${f}_${_act_DEFAULT_FROM}} ${val}") + endif() + endif() + message(STATUS "config: CMAKE_${f}_${UNAME} --- ${val}") + set(CMAKE_${f}_${UNAME} "${val}" CACHE STRING "" FORCE) + mark_as_advanced(CMAKE_${f}_${UNAME}) + if(_act_SET_MAIN_FLAGS) + set(CMAKE_${f} "${CMAKE_${f}_${UNAME}}" CACHE STRING "" FORCE) + endif() + endforeach() + endif() + +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/CreateSourceGroup.cmake b/thirdparty/ryml/ext/c4core/cmake/CreateSourceGroup.cmake new file mode 100644 index 000000000..e8e144184 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/CreateSourceGroup.cmake @@ -0,0 +1,31 @@ +# create hierarchical source groups based on a dir tree +# +# EXAMPLE USAGE: +# +# create_source_group("src" "${SRC_ROOT}" "${SRC_LIST}") +# +# Visual Studio usually has the equivalent to this: +# +# create_source_group("Header Files" ${PROJ_SRC_DIR} "${PROJ_HEADERS}") +# create_source_group("Source Files" ${PROJ_SRC_DIR} "${PROJ_SOURCES}") +# +# TODO: <jpmag> this was taken from a stack overflow answer. Need to find it +# and add a link here. + +macro(create_source_group GroupPrefix RootDir ProjectSources) + set(DirSources ${ProjectSources}) + foreach(Source ${DirSources}) + #message(STATUS "s=${Source}") + string(REGEX REPLACE "([\\^\\$*+?|])" "\\\\\\1" RootDirRegex "${RootDir}") + string(REGEX REPLACE "${RootDirRegex}" "" RelativePath "${Source}") + #message(STATUS " ${RelativePath}") + string(REGEX REPLACE "[\\\\/][^\\\\/]*$" "" RelativePath "${RelativePath}") + #message(STATUS " ${RelativePath}") + string(REGEX REPLACE "^[\\\\/]" "" RelativePath "${RelativePath}") + #message(STATUS " ${RelativePath}") + string(REGEX REPLACE "/" "\\\\\\\\" RelativePath "${RelativePath}") + #message(STATUS " ${RelativePath}") + source_group("${GroupPrefix}\\${RelativePath}" FILES ${Source}) + #message(STATUS " ${Source}") + endforeach(Source) +endmacro(create_source_group) diff --git a/thirdparty/ryml/ext/c4core/cmake/Doxyfile.full.in b/thirdparty/ryml/ext/c4core/cmake/Doxyfile.full.in new file mode 100644 index 000000000..f444cb742 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Doxyfile.full.in @@ -0,0 +1,2566 @@ +# Doxyfile 1.8.15 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = @_PROJ@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = @_PROJ_BRIEF@ + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @_OUTPUT_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = YES + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = @_STRIP_FROM_PATH@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = @_STRIP_FROM_INC_PATH@ + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 4 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = YES + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = YES + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @_INPUT@ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice \ + @_FILE_PATTERNS@ + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = @_EXCLUDE@ + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = @_EXCLUDE_PATTERNS@ + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = @_EXCLUDE_SYMBOLS@ + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = @_EXAMPLE_PATH@ + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = @_CLANG_DATABASE_PATH@ + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: \makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = \makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = YES + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES diff --git a/thirdparty/ryml/ext/c4core/cmake/Doxyfile.in b/thirdparty/ryml/ext/c4core/cmake/Doxyfile.in new file mode 100644 index 000000000..6b464bf40 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Doxyfile.in @@ -0,0 +1,2566 @@ +# Doxyfile 1.8.15 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = @_PROJ@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = @_PROJ_BRIEF@ + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @_OUTPUT_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = YES + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = @_STRIP_FROM_PATH@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = @_STRIP_FROM_INC_PATH@ + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 4 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = YES + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = YES + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @_INPUT@ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice \ + @_FILE_PATTERNS@ + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = @_EXCLUDE@ + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = @_EXCLUDE_PATTERNS@ + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = @_EXCLUDE_SYMBOLS@ + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = @_EXAMPLE_PATH@ + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = @_CLANG_DATABASE_PATH@ + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via Javascript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have Javascript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: https://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: \makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = \makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber, +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES, to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = YES + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the +# program listings (including syntax highlighting and cross-referencing +# information) to the DOCBOOK output. Note that enabling this will significantly +# increase the size of the DOCBOOK output. +# The default value is: NO. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_PROGRAMLISTING = NO + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file. If left blank, it is assumed +# PlantUML is not used or called during a preprocessing step. Doxygen will +# generate a warning when it encounters a \startuml command in this case and +# will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES diff --git a/thirdparty/ryml/ext/c4core/cmake/ExternalProjectUtils.cmake b/thirdparty/ryml/ext/c4core/cmake/ExternalProjectUtils.cmake new file mode 100644 index 000000000..f1328c6df --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/ExternalProjectUtils.cmake @@ -0,0 +1,215 @@ +# (C) 2017 Joao Paulo Magalhaes <[email protected]> + +include(CMakeParseArguments) + +#------------------------------------------------------------------------------ +# Usage: +# +# ExternalProject_GetFwdArgs(output_var +# [NO_DEFAULTS] +# [VARS var1 var2 ...] +# [EXCLUDE xvar1 xvar2 ...] +# [QUIET] +# ) +# +# Get the current cmake environment in a sequence of -DVAR=${VAR} +# tokens so that the environment can be forwarded to an external +# cmake project through CMAKE_ARGS. +# +# Example: +# ExternalProject_GetFwdArgs(FWD_ARGS) +# ExternalProject_Add(foo SOURCE_DIR ../foo +# CMAKE_ARGS ${FWD_ARGS} +# ... etc) +# +# Use this function to enable forwarding the current cmake environment +# to an external project. It outputs all the needed variables in the +# form of a sequence of -DVAR=value, suitable for use in the CMAKE_ARGS +# clause of ExternalProject_Add(). +# +# This function uses ExternalProject_GetFwdVarNames() to find out the +# list of variables to export. If this behaviour does not fit your +# needs you can: +# +# * append more of your own variables (using the VARS +# argument). The vars specified in this option will each be +# added to the output in the form of -Dvar=${var} +# +# * you can also avoid any defaults obtained through usage of +# ExternalProject_GetFwdNames() by specifying NO_DEFAULTS. +# +# Example with custom variable names (adding more): +# ExternalProject_GetFwdVarNames(FWD_ARGS VARS USER_VAR1 USER_VAR2) +# ExternalProjectAdd(foo SOURCE_DIR ../foo CMAKE_ARGS ${FWD_ARGS}) +# +# Example with custom variable names (just your own): +# ExternalProject_GetFwdVarNames(FWD_ARGS NO_DEFAULTS VARS USER_VAR1 USER_VAR2) +# ExternalProjectAdd(foo SOURCE_DIR ../foo CMAKE_ARGS ${FWD_ARGS}) +# +function(ExternalProject_GetFwdArgs output_var) + set(options0arg + NO_DEFAULTS + QUIET + ) + set(options1arg + ) + set(optionsnarg + VARS + EXCLUDE + ) + cmake_parse_arguments(_epgfa "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN}) + if(NOT _epfga_NO_DEFAULTS) + ExternalProject_GetFwdVarNames(_fwd_names) + endif() + if(_epgfa_VARS) + list(APPEND _fwd_names ${_epgfa_VARS}) + endif() + if(_epgfa_EXCLUDE) + list(REMOVE_ITEM _fwd_names ${_epgfa_EXCLUDE}) + endif() + set(_epgfa_args) + foreach(_f ${_fwd_names}) + if(${_f}) + list(APPEND _epgfa_args -D${_f}=${${_f}}) + if(NOT _epfga_QUIET) + message(STATUS "ExternalProject_GetFwdArgs: ${_f}=${${_f}}") + endif() + endif() + endforeach() + set(${output_var} "${_epgfa_args}" PARENT_SCOPE) +endfunction(ExternalProject_GetFwdArgs) + + +#------------------------------------------------------------------------------ +# Gets a default list with the names of variables to forward to an +# external project. This function creates a list of common cmake +# variable names which have an impact in the output binaries or their +# placement. +function(ExternalProject_GetFwdVarNames output_var) + # these common names are irrespective of build type + set(names + CMAKE_GENERATOR + CMAKE_INSTALL_PREFIX + CMAKE_ARCHIVE_OUTPUT_DIRECTORY + CMAKE_LIBRARY_OUTPUT_DIRECTORY + CMAKE_RUNTIME_OUTPUT_DIRECTORY + CMAKE_AR + CMAKE_BUILD_TYPE + CMAKE_INCLUDE_PATH + CMAKE_LIBRARY_PATH + #CMAKE_MODULE_PATH # this is dangerous as it can override the external project's build files. + CMAKE_PREFIX_PATH + BUILD_SHARED_LIBS + CMAKE_CXX_COMPILER + CMAKE_C_COMPILER + CMAKE_LINKER + CMAKE_MAKE_PROGRAM + CMAKE_NM + CMAKE_OBJCOPY + CMAKE_RANLIB + CMAKE_STRIP + CMAKE_TOOLCHAIN_FILE + #CMAKE_CONFIGURATION_TYPES # not this. external projects will have their own build configurations + ) + # these names have per-build type values; + # use CMAKE_CONFIGURATION_TYPES to construct the list + foreach(v + CMAKE_CXX_FLAGS + CMAKE_C_FLAGS + CMAKE_EXE_LINKER_FLAGS + CMAKE_MODULE_LINKER_FLAGS + CMAKE_SHARED_LINKER_FLAGS) + list(APPEND names ${v}) + foreach(t ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER ${t} u) + list(APPEND names ${v}_${u}) + endforeach() + endforeach() + set(${output_var} "${names}" PARENT_SCOPE) +endfunction(ExternalProject_GetFwdVarNames) + + +#------------------------------------------------------------------------------ +macro(ExternalProject_Import name) + set(options0arg + ) + set(options1arg + PREFIX # look only here when findind + ) + set(optionsnarg + INCLUDE_PATHS # use these dirs for searching includes + LIBRARY_PATHS # use these dirs for searching libraries + INCLUDES # find these includes and append them to ${name}_INCLUDE_DIRS + INCLUDE_DIR_SUFFIXES + LIBRARIES # find these libs and append them to ${name}_LIBRARIES + LIBRARY_DIR_SUFFIXES + ) + cmake_parse_arguments(_eep "${options0arg}" "${options1arg}" "${optionsnarg}" ${ARGN}) + + if(NOT _eep_PREFIX) + message(FATAL_ERROR "no prefix was given") + endif() + + include(FindPackageHandleStandardArgs) + + #---------------------------------------------------------------- + # includes + + # the list of paths to search for includes + set(_eep_ipaths ${_eep_PREFIX}) + foreach(_eep_i ${_eep_INCLUDE_DIRS}) + list(APPEND _eep_ipaths ${__eep_PREFIX}/${_eep_i}) + endforeach() + + # find the includes that were asked for, and add + # their paths to the includes list + set(_eep_idirs) + foreach(_eep_i ${_eep_INCLUDES}) + find_path(_eep_path_${_eep_i} ${_eep_i} + PATHS ${_eep_ipaths} + PATH_SUFFIXES include ${_eep_INCLUDE_DIR_SUFFIXES} + NO_DEFAULT_PATH + ) + if(NOT _eep_path_${_eep_i}) + message(FATAL_ERROR "could not find include: ${_eep_i}") + endif() + #message(STATUS "include: ${_eep_i} ---> ${_eep_path_${_eep_i}}") + list(APPEND _eep_idirs ${_eep_path_${_eep_i}}) + find_package_handle_standard_args(${_eep_i}_INCLUDE_DIR DEFAULT_MSG _eep_path_${_eep_i}) + endforeach() + if(_eep_idirs) + list(REMOVE_DUPLICATES _eep_idirs) + endif() + + # save the include list + set(${name}_INCLUDE_DIRS "${_eep_idirs}" CACHE STRING "" FORCE) + + #---------------------------------------------------------------- + # libraries + + # the list of paths to search for libraries + set(_eep_lpaths ${_eep_PREFIX}) + foreach(_eep_i ${_eep_LIBRARIES}) + list(APPEND _eep_lpaths ${__eep_PREFIX}/${_eep_i}) + endforeach() + + # find any libraries that were asked for + set(_eep_libs) + foreach(_eep_i ${_eep_LIBRARIES}) + find_library(_eep_lib_${_eep_i} ${_eep_i} + PATHS ${_eep_lpaths} + PATH_SUFFIXES lib ${_eep_LIBRARY_DIR_SUFFIXES} + NO_DEFAULT_PATH + ) + if(NOT _eep_lib_${_eep_i}) + message(FATAL_ERROR "could not find library: ${_eep_i}") + endif() + #message(STATUS "lib: ${_eep_i} ---> ${_eep_lib_${_eep_i}}") + list(APPEND _eep_libs ${_eep_lib_${_eep_i}}) + find_package_handle_standard_args(${_eep_i}_LIBRARY DEFAULT_MSG _eep_lib_${_eep_i}) + endforeach() + + # save the include list + set(${name}_LIBRARIES ${_eep_libs} CACHE STRING "") + +endmacro(ExternalProject_Import) diff --git a/thirdparty/ryml/ext/c4core/cmake/FindD3D12.cmake b/thirdparty/ryml/ext/c4core/cmake/FindD3D12.cmake new file mode 100644 index 000000000..01e7a3ae9 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/FindD3D12.cmake @@ -0,0 +1,75 @@ +# Find the win10 SDK path. +if ("$ENV{WIN10_SDK_PATH}$ENV{WIN10_SDK_VERSION}" STREQUAL "" ) + get_filename_component(WIN10_SDK_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0;InstallationFolder]" ABSOLUTE CACHE) + get_filename_component(TEMP_WIN10_SDK_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0;ProductVersion]" ABSOLUTE CACHE) + get_filename_component(WIN10_SDK_VERSION ${TEMP_WIN10_SDK_VERSION} NAME) +elseif(TRUE) + set (WIN10_SDK_PATH $ENV{WIN10_SDK_PATH}) + set (WIN10_SDK_VERSION $ENV{WIN10_SDK_VERSION}) +endif ("$ENV{WIN10_SDK_PATH}$ENV{WIN10_SDK_VERSION}" STREQUAL "" ) + +# WIN10_SDK_PATH will be something like C:\Program Files (x86)\Windows Kits\10 +# WIN10_SDK_VERSION will be something like 10.0.14393 or 10.0.14393.0; we need the +# one that matches the directory name. + +if (IS_DIRECTORY "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}.0") + set(WIN10_SDK_VERSION "${WIN10_SDK_VERSION}.0") +endif (IS_DIRECTORY "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}.0") + + +# Find the d3d12 and dxgi include path, it will typically look something like this. +# C:\Program Files (x86)\Windows Kits\10\Include\10.0.10586.0\um\d3d12.h +# C:\Program Files (x86)\Windows Kits\10\Include\10.0.10586.0\shared\dxgi1_4.h +find_path(D3D12_INCLUDE_DIR # Set variable D3D12_INCLUDE_DIR + d3d12.h # Find a path with d3d12.h + HINTS "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}/um" + DOC "path to WIN10 SDK header files" + HINTS + ) + +find_path(DXGI_INCLUDE_DIR # Set variable DXGI_INCLUDE_DIR + dxgi1_4.h # Find a path with dxgi1_4.h + HINTS "${WIN10_SDK_PATH}/Include/${WIN10_SDK_VERSION}/shared" + DOC "path to WIN10 SDK header files" + HINTS + ) + +if ("${DXC_BUILD_ARCH}" STREQUAL "x64" ) + find_library(D3D12_LIBRARY NAMES d3d12.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x64 ) +elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM") + find_library(D3D12_LIBRARY NAMES d3d12.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm ) +elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM64" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM64") + find_library(D3D12_LIBRARY NAMES d3d12.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm64 ) +elseif ("${DXC_BUILD_ARCH}" STREQUAL "Win32" ) + find_library(D3D12_LIBRARY NAMES d3d12.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x86 ) +endif ("${DXC_BUILD_ARCH}" STREQUAL "x64" ) + +if ("${DXC_BUILD_ARCH}" STREQUAL "x64" ) + find_library(DXGI_LIBRARY NAMES dxgi.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x64 ) +elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM") + find_library(DXGI_LIBRARY NAMES dxgi.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm ) +elseif (CMAKE_GENERATOR MATCHES "Visual Studio.*ARM64" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM64") + find_library(DXGI_LIBRARY NAMES dxgi.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/arm64 ) +elseif ("${DXC_BUILD_ARCH}" STREQUAL "Win32" ) + find_library(DXGI_LIBRARY NAMES dxgi.lib + HINTS ${WIN10_SDK_PATH}/Lib/${WIN10_SDK_VERSION}/um/x86 ) +endif ("${DXC_BUILD_ARCH}" STREQUAL "x64" ) + +set(D3D12_LIBRARIES ${D3D12_LIBRARY} ${DXGI_LIBRARY}) +set(D3D12_INCLUDE_DIRS ${D3D12_INCLUDE_DIR} ${DXGI_INCLUDE_DIR}) + + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set D3D12_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(D3D12 DEFAULT_MSG + D3D12_INCLUDE_DIRS D3D12_LIBRARIES) + +mark_as_advanced(D3D12_INCLUDE_DIRS D3D12_LIBRARIES) diff --git a/thirdparty/ryml/ext/c4core/cmake/FindDX12.cmake b/thirdparty/ryml/ext/c4core/cmake/FindDX12.cmake new file mode 100644 index 000000000..5ae79ec07 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/FindDX12.cmake @@ -0,0 +1,76 @@ +# Attempt to find the D3D12 libraries +# Defines: +# +# DX12_FOUND - system has DX12 +# DX12_INCLUDE_PATH - path to the DX12 headers +# DX12_LIBRARIES - path to the DX12 libraries +# DX12_LIB - d3d12.lib + +set(DX12_FOUND "NO") + +if(WIN32) + set(WIN10_SDK_DIR "C:/Program Files (x86)/Windows Kits/10") + #set(WIN10_SDK_VERSION "10.0.10069.0") + file(GLOB WIN10_SDK_VERSIONS + LIST_DIRECTORIES TRUE + RELATIVE "${WIN10_SDK_DIR}/Lib" + "${WIN10_SDK_DIR}/Lib/*") + list(SORT WIN10_SDK_VERSIONS) + list(GET WIN10_SDK_VERSIONS -1 WIN10_SDK_VERSION) + + if(CMAKE_CL_64) + set(w10ARCH x64) + elseif(CMAKE_GENERATOR MATCHES "Visual Studio.*ARM" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM") + set(w10ARCH arm) + elseif(CMAKE_GENERATOR MATCHES "Visual Studio.*ARM64" OR "${DXC_BUILD_ARCH}" STREQUAL "ARM64") + set(w10ARCH arm64) + else() + set(w10ARCH x86) + endif() + + # Look for the windows 8 sdk + find_path(DX12_INC_DIR + NAMES d3d12.h + PATHS "${WIN10_SDK_DIR}/Include/${WIN10_SDK_VERSION}/um" + DOC "Path to the d3d12.h file" + ) + find_path(DXGI_INC_DIR + NAMES dxgi1_4.h + PATHS "${WIN10_SDK_DIR}/Include/${WIN10_SDK_VERSION}/shared" + DOC "Path to the dxgi header file" + ) + + if(DX12_INC_DIR AND DXGI_INC_DIR) + find_library(DX12_LIB + NAMES d3d12 + PATHS "${WIN10_SDK_DIR}/Lib/${WIN10_SDK_VERSION}/um/${w10ARCH}" + NO_DEFAULT_PATH + DOC "Path to the d3d12.lib file" + ) + find_library(DXGI_LIB + NAMES dxgi + PATHS "${WIN10_SDK_DIR}/Lib/${WIN10_SDK_VERSION}/um/${w10ARCH}" + NO_DEFAULT_PATH + DOC "Path to the dxgi.lib file" + ) + if(DX12_LIB AND DXGI_LIB) + set(DX12_FOUND "YES") + set(DX12_LIBRARIES ${DX12_LIB} ${DXGI_LIB}) + mark_as_advanced(DX12_INC_DIR DX12_LIB) + mark_as_advanced(DXGI_INC_DIR DXGI_LIB) + endif() + endif() +endif(WIN32) + +if(DX12_FOUND) + if(NOT DX12_FIND_QUIETLY) + message(STATUS "DX12 headers found at ${DX12_INC_DIR}") + endif() +else() + if(DX12_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find Direct3D12") + endif() + if(NOT DX12_FIND_QUIETLY) + message(STATUS "Could NOT find Direct3D12") + endif() +endif() diff --git a/thirdparty/ryml/ext/c4core/cmake/GetFlags.cmake b/thirdparty/ryml/ext/c4core/cmake/GetFlags.cmake new file mode 100644 index 000000000..e7e9e5aa6 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/GetFlags.cmake @@ -0,0 +1,53 @@ + +function(_c4_intersperse_with_flag outvar flag) + if(MSVC AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # it may be clang as well + set(f "/${flag}") + else() + set(f "-${flag}") + endif() + set(out) + foreach(i ${ARGN}) + if(NOT "${i}" STREQUAL "") + set(out "${out} ${f} '${i}'") + + # ... Following this are several unsuccessful attempts to make + # sure that an empty generator expression passed as part of the + # arguments won't be expanded to nothing between successive + # flags. For example, -I /some/include -I -I /other/include, + # which is wrong as it misses an empty quote. This causes + # clang-tidy in particular to fail. Maybe this is happening + # because the result is passed to separate_arguments() which + # prevents the lists from being evaluated correctly. Also, note + # that add_custom_target() has the following options which may + # help: COMMAND_EXPAND_LISTS and VERBATIM. + + # Anyway -- for now it is working, but maybe the generator + # expression approach turns out to work while being much cleaner + # than the current approach. + + #set(c $<GENEX_EVAL,$<BOOL:${i}>>) + #set(c $<BOOL:${i}>) # i may be a generator expression the evaluates to empty + #set(s "${f} ${i}") + #set(e "${f} aaaaaaWTF") + #list(APPEND out $<IF:${c},${s},${e}>) + #list(APPEND out $<${c},${s}>) + #list(APPEND out $<GENEX_EVAL:${c},${s}>) + #list(APPEND out $<TARGET_GENEX_EVAL:${tgt},${c},${s}>) + endif() + endforeach() + ## https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#string-valued-generator-expressions + #if(ARGN) + # set(out "${f}$<JOIN:${ARGN},;${f}>") + #endif() + set(${outvar} ${out} PARENT_SCOPE) +endfunction() + +function(c4_get_define_flags outvar) + _c4_intersperse_with_flag(out D ${ARGN}) + set(${outvar} ${out} PARENT_SCOPE) +endfunction() + +function(c4_get_include_flags outvar) + _c4_intersperse_with_flag(out I ${ARGN}) + set(${outvar} ${out} PARENT_SCOPE) +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/GetNames.cmake b/thirdparty/ryml/ext/c4core/cmake/GetNames.cmake new file mode 100644 index 000000000..a287372c0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/GetNames.cmake @@ -0,0 +1,51 @@ +function(get_lib_names lib_names base) + set(${lib_names}) + foreach(__glnname ${ARGN}) + if(WIN32) + set(__glnn ${__glnname}.lib) + else() + set(__glnn lib${__glnname}.a) + endif() + list(APPEND ${lib_names} "${base}${__glnn}") + endforeach() + set(lib_names ${lib_names} PARENT_SCOPE) +endfunction() + +function(get_dll_names dll_names base) + set(${dll_names}) + foreach(__glnname ${ARGN}) + if(WIN32) + set(__glnn ${__glnname}.dll) + else() + set(__glnn lib${__glnname}.so) + endif() + list(APPEND ${dll_names} "${base}${__glnn}") + endforeach() + set(dll_names ${dll_names} PARENT_SCOPE) +endfunction() + +function(get_script_names script_names base) + set(${script_names}) + foreach(__glnname ${ARGN}) + if(WIN32) + set(__glnn ${__glnname}.bat) + else() + set(__glnn ${__glnname}.sh) + endif() + list(APPEND ${script_names} "${base}${__glnn}") + endforeach() + set(script_names ${script_names} PARENT_SCOPE) +endfunction() + +function(get_exe_names exe_names base) + set(${exe_names}) + foreach(__glnname ${ARGN}) + if(WIN32) + set(__glnn ${__glnname}.exe) + else() + set(__glnn ${__glnname}) + endif() + list(APPEND ${exe_names} "${base}${__glnn}") + endforeach() + set(exe_names ${exe_names} PARENT_SCOPE) +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/LICENSE.txt b/thirdparty/ryml/ext/c4core/cmake/LICENSE.txt new file mode 100644 index 000000000..47b6b4394 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2018, Joao Paulo Magalhaes <[email protected]> + +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/thirdparty/ryml/ext/c4core/cmake/PVS-Studio.cmake b/thirdparty/ryml/ext/c4core/cmake/PVS-Studio.cmake new file mode 100644 index 000000000..f0d217c2b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/PVS-Studio.cmake @@ -0,0 +1,275 @@ +# 2006-2008 (c) Viva64.com Team +# 2008-2016 (c) OOO "Program Verification Systems" +# +# Version 2 + +function (pvs_studio_relative_path VAR ROOT FILEPATH) + set("${VAR}" "${FILEPATH}" PARENT_SCOPE) + if ("${FILEPATH}" MATCHES "^/.*$") + file(RELATIVE_PATH RPATH "${ROOT}" "${FILEPATH}") + if (NOT "${RPATH}" MATCHES "^\\.\\..*$") + set("${VAR}" "${RPATH}" PARENT_SCOPE) + endif () + endif () +endfunction () + +function (pvs_studio_join_path VAR DIR1 DIR2) + if ("${DIR2}" MATCHES "^(/|~).*$" OR "${DIR1}" STREQUAL "") + set("${VAR}" "${DIR2}" PARENT_SCOPE) + else () + set("${VAR}" "${DIR1}/${DIR2}" PARENT_SCOPE) + endif () +endfunction () + +macro (pvs_studio_append_flags_from_property CXX C DIR PREFIX) + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (PROP ${PROPERTY}) + pvs_studio_join_path(PROP "${DIR}" "${PROP}") + list(APPEND "${CXX}" "${PREFIX}${PROP}") + list(APPEND "${C}" "${PREFIX}${PROP}") + endforeach () + endif () +endmacro () + +macro (pvs_studio_append_standard_flag FLAGS STANDARD) + if ("${STANDARD}" MATCHES "^(99|11|14|17)$") + if ("${PVS_STUDIO_PREPROCESSOR}" MATCHES "gcc|clang") + list(APPEND "${FLAGS}" "-std=c++${STANDARD}") + endif () + endif () +endmacro () + +function (pvs_studio_set_directory_flags DIRECTORY CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" INCLUDE_DIRECTORIES) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" COMPILE_DEFINITIONS) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_set_target_flags TARGET CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + get_target_property(PROPERTY "${TARGET}" INCLUDE_DIRECTORIES) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I") + + get_target_property(PROPERTY "${TARGET}" COMPILE_DEFINITIONS) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D") + + get_target_property(PROPERTY "${TARGET}" CXX_STANDARD) + pvs_studio_append_standard_flag(CXX_FLAGS "${PROPERTY}") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_set_source_file_flags SOURCE) + set(LANGUAGE "") + + string(TOLOWER "${SOURCE}" SOURCE_LOWER) + if ("${LANGUAGE}" STREQUAL "" AND "${SOURCE_LOWER}" MATCHES "^.*\\.(c|cpp|cc|cx|cxx|cp|c\\+\\+)$") + if ("${SOURCE}" MATCHES "^.*\\.c$") + set(LANGUAGE C) + else () + set(LANGUAGE CXX) + endif () + endif () + + if ("${LANGUAGE}" STREQUAL "C") + set(CL_PARAMS ${PVS_STUDIO_C_FLAGS} ${PVS_STUDIO_TARGET_C_FLAGS} -DPVS_STUDIO) + elseif ("${LANGUAGE}" STREQUAL "CXX") + set(CL_PARAMS ${PVS_STUDIO_CXX_FLAGS} ${PVS_STUDIO_TARGET_CXX_FLAGS} -DPVS_STUDIO) + endif () + + set(PVS_STUDIO_LANGUAGE "${LANGUAGE}" PARENT_SCOPE) + set(PVS_STUDIO_CL_PARAMS "${CL_PARAMS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_analyze_file SOURCE SOURCE_DIR BINARY_DIR) + set(PLOGS ${PVS_STUDIO_PLOGS}) + pvs_studio_set_source_file_flags("${SOURCE}") + + get_filename_component(SOURCE "${SOURCE}" REALPATH) + pvs_studio_relative_path(SOURCE_RELATIVE "${SOURCE_DIR}" "${SOURCE}") + pvs_studio_join_path(SOURCE "${SOURCE_DIR}" "${SOURCE}") + + set(LOG "${BINARY_DIR}/PVS-Studio/${SOURCE_RELATIVE}.plog") + get_filename_component(LOG "${LOG}" REALPATH) + get_filename_component(PARENT_DIR "${LOG}" DIRECTORY) + + if (EXISTS "${SOURCE}" AND NOT TARGET "${LOG}" AND NOT "${PVS_STUDIO_LANGUAGE}" STREQUAL "") + add_custom_command(OUTPUT "${LOG}" + COMMAND mkdir -p "${PARENT_DIR}" + COMMAND rm -f "${LOG}" + COMMAND "${PVS_STUDIO_BIN}" analyze + --output-file "${LOG}" + --source-file "${SOURCE}" + ${PVS_STUDIO_ARGS} + --cl-params ${PVS_STUDIO_CL_PARAMS} "${SOURCE}" + WORKING_DIRECTORY "${BINARY_DIR}" + DEPENDS "${SOURCE}" "${PVS_STUDIO_CONFIG}" + VERBATIM + COMMENT "Analyzing ${PVS_STUDIO_LANGUAGE} file ${SOURCE_RELATIVE}") + list(APPEND PLOGS "${LOG}") + endif () + set(PVS_STUDIO_PLOGS "${PLOGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_analyze_target TARGET DIR) + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}") + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + + get_target_property(PROPERTY "${TARGET}" SOURCES) + pvs_studio_relative_path(BINARY_DIR "${CMAKE_SOURCE_DIR}" "${DIR}") + if ("${BINARY_DIR}" MATCHES "^/.*$") + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "PVS-Studio/__${BINARY_DIR}") + else () + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "${BINARY_DIR}") + endif () + + file(MAKE_DIRECTORY "${BINARY_DIR}") + + pvs_studio_set_directory_flags("${DIR}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + pvs_studio_set_target_flags("${TARGET}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (SOURCE ${PROPERTY}) + pvs_studio_join_path(SOURCE "${DIR}" "${SOURCE}") + pvs_studio_analyze_file("${SOURCE}" "${DIR}" "${BINARY_DIR}") + endforeach () + endif () + + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_add_target) + macro (default VAR VALUE) + if ("${${VAR}}" STREQUAL "") + set("${VAR}" "${VALUE}") + endif () + endmacro () + + set(PVS_STUDIO_SUPPORTED_PREPROCESSORS "gcc|clang") + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(DEFAULT_PREPROCESSOR "clang") + else () + set(DEFAULT_PREPROCESSOR "gcc") + endif () + + set(OPTIONAL OUTPUT ALL) + set(SINGLE LICENSE CONFIG TARGET LOG FORMAT BIN CONVERTER PLATFORM PREPROCESSOR CFG_TEXT) + set(MULTI SOURCES C_FLAGS CXX_FLAGS ARGS DEPENDS ANALYZE) + cmake_parse_arguments(PVS_STUDIO "${OPTIONAL}" "${SINGLE}" "${MULTI}" ${ARGN}) + + if ("${PVS_STUDIO_CFG}" STREQUAL "" OR NOT "${PVS_STUDIO_CFG_TEXT}" STREQUAL "") + set(PVS_STUDIO_EMPTY_CONFIG ON) + else () + set(PVS_STUDIO_EMPTY_CONFIG OFF) + endif () + + default(PVS_STUDIO_CFG_TEXT "analysis-mode=4") + default(PVS_STUDIO_CONFIG "${CMAKE_BINARY_DIR}/PVS-Studio.cfg") + default(PVS_STUDIO_C_FLAGS "") + default(PVS_STUDIO_CXX_FLAGS "") + default(PVS_STUDIO_TARGET "pvs") + default(PVS_STUDIO_LOG "PVS-Studio.log") + default(PVS_STUDIO_BIN "pvs-studio-analyzer") + default(PVS_STUDIO_CONVERTER "plog-converter") + default(PVS_STUDIO_PREPROCESSOR "${DEFAULT_PREPROCESSOR}") + default(PVS_STUDIO_PLATFORM "linux64") + + if (PVS_STUDIO_EMPTY_CONFIG) + set(PVS_STUDIO_CONFIG_COMMAND echo "${PVS_STUDIO_CFG_TEXT}" > "${PVS_STUDIO_CONFIG}") + else () + set(PVS_STUDIO_CONFIG_COMMAND touch "${PVS_STUDIO_CONFIG}") + endif () + + add_custom_command(OUTPUT "${PVS_STUDIO_CONFIG}" + COMMAND ${PVS_STUDIO_CONFIG_COMMAND} + WORKING_DIRECTORY "${BINARY_DIR}" + COMMENT "Generating PVS-Studio.cfg") + + + if (NOT "${PVS_STUDIO_PREPROCESSOR}" MATCHES "^${PVS_STUDIO_SUPPORTED_PREPROCESSORS}$") + message(FATAL_ERROR "Preprocessor ${PVS_STUDIO_PREPROCESSOR} isn't supported. Available options: ${PVS_STUDIO_SUPPORTED_PREPROCESSORS}.") + endif () + + pvs_studio_append_standard_flag(PVS_STUDIO_CXX_FLAGS "${CMAKE_CXX_STANDARD}") + pvs_studio_set_directory_flags("${CMAKE_CURRENT_SOURCE_DIR}" PVS_STUDIO_CXX_FLAGS PVS_STUDIO_C_FLAGS) + + if (NOT "${PVS_STUDIO_LICENSE}" STREQUAL "") + pvs_studio_join_path(PVS_STUDIO_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}" "${PVS_STUDIO_LICENSE}") + list(APPEND PVS_STUDIO_ARGS --lic-file "${PVS_STUDIO_LICENSE}") + endif () + list(APPEND PVS_STUDIO_ARGS --cfg "${PVS_STUDIO_CONFIG}" + --platform "${PVS_STUDIO_PLATFORM}" + --preprocessor "${PVS_STUDIO_PREPROCESSOR}") + + set(PVS_STUDIO_PLOGS "") + + foreach (TARGET ${PVS_STUDIO_ANALYZE}) + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}") + string(FIND "${TARGET}" ":" DELIM) + if ("${DELIM}" GREATER "-1") + math(EXPR DELIMI "${DELIM}+1") + string(SUBSTRING "${TARGET}" "${DELIMI}" "-1" DIR) + string(SUBSTRING "${TARGET}" "0" "${DELIM}" TARGET) + pvs_studio_join_path(DIR "${CMAKE_CURRENT_SOURCE_DIR}" "${DIR}") + endif () + pvs_studio_analyze_target("${TARGET}" "${DIR}") + list(APPEND PVS_STUDIO_DEPENDS "${TARGET}") + endforeach () + + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + foreach (SOURCE ${PVS_STUDIO_SOURCES}) + pvs_studio_analyze_file("${SOURCE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") + endforeach () + + pvs_studio_relative_path(LOG_RELATIVE "${CMAKE_BINARY_DIR}" "${PVS_STUDIO_LOG}") + if (PVS_STUDIO_PLOGS) + set(COMMANDS COMMAND cat ${PVS_STUDIO_PLOGS} > "${PVS_STUDIO_LOG}") + set(COMMENT "Generating ${LOG_RELATIVE}") + if (NOT "${PVS_STUDIO_FORMAT}" STREQUAL "" OR PVS_STUDIO_OUTPUT) + if ("${PVS_STUDIO_FORMAT}" STREQUAL "") + set(PVS_STUDIO_FORMAT "errorfile") + endif () + list(APPEND COMMANDS + COMMAND mv "${PVS_STUDIO_LOG}" "${PVS_STUDIO_LOG}.pvs.raw" + COMMAND "${PVS_STUDIO_CONVERTER}" -t "${PVS_STUDIO_FORMAT}" "${PVS_STUDIO_LOG}.pvs.raw" -o "${PVS_STUDIO_LOG}" + COMMAND rm -f "${PVS_STUDIO_LOG}.pvs.raw") + endif () + else () + set(COMMANDS COMMAND touch "${PVS_STUDIO_LOG}") + set(COMMENT "Generating ${LOG_RELATIVE}: no sources found") + endif () + + add_custom_command(OUTPUT "${PVS_STUDIO_LOG}" + ${COMMANDS} + COMMENT "${COMMENT}" + DEPENDS ${PVS_STUDIO_PLOGS} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + + if (PVS_STUDIO_ALL) + set(ALL "ALL") + else () + set(ALL "") + endif () + + if (PVS_STUDIO_OUTPUT) + set(COMMANDS COMMAND cat "${PVS_STUDIO_LOG}" 1>&2) + else () + set(COMMANDS "") + endif () + + add_custom_target("${PVS_STUDIO_TARGET}" ${ALL} ${COMMANDS} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" DEPENDS ${PVS_STUDIO_DEPENDS} "${PVS_STUDIO_LOG}") +endfunction () + diff --git a/thirdparty/ryml/ext/c4core/cmake/PatchUtils.cmake b/thirdparty/ryml/ext/c4core/cmake/PatchUtils.cmake new file mode 100644 index 000000000..164940e00 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/PatchUtils.cmake @@ -0,0 +1,25 @@ +# create a script that applies a patch (it's different in windows) + +# to generate a patch: +# subversion: svn diff --patch-compatible > path/to/the/patch.diff + + +function(apply_patch patch where mark) + if(NOT EXISTS "${mark}") + if(NOT Patch_EXECUTABLE) + find_package(Patch REQUIRED) + endif() + file(TO_NATIVE_PATH ${patch} patch_native) + get_filename_component(patch_name "${patch}" NAME) + message(STATUS "Applying patch: ${patch_name}") + execute_process( + COMMAND "${Patch_EXECUTABLE}" "-p0" "--input=${patch_native}" + WORKING_DIRECTORY "${where}" + RESULT_VARIABLE status) + if(NOT status STREQUAL "0") + message(FATAL_ERROR "could not apply patch: ${patch} ---> ${where}") + else() + file(TOUCH "${mark}") + endif() + endif() +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/PrintVar.cmake b/thirdparty/ryml/ext/c4core/cmake/PrintVar.cmake new file mode 100644 index 000000000..acd04e55b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/PrintVar.cmake @@ -0,0 +1,27 @@ +function(status) + message(STATUS "${ARGV}") +endfunction() + +function(print_var var) + message(STATUS "${var}=${${var}} ${ARGN}") +endfunction() + +function(print_vars) + foreach(a ${ARGN}) + message(STATUS "${a}=${${a}}") + endforeach(a) +endfunction() + +function(debug_var debug var) + if(${debug}) + message(STATUS "${var}=${${var}} ${ARGN}") + endif() +endfunction() + +function(debug_vars debug) + if(${debug}) + foreach(a ${ARGN}) + message(STATUS "${a}=${${a}}") + endforeach(a) + endif() +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/README.md b/thirdparty/ryml/ext/c4core/cmake/README.md new file mode 100644 index 000000000..13341daa0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/README.md @@ -0,0 +1,25 @@ +# cmake project utilities + +Useful cmake scripts, at [c4Project.cmake](c4Project.cmake). + +## Project utilities + +## Adding targets + +### Target types + +## Downloading and configuring third-party projects at configure time + +## Setting up tests + +### Coverage +### Static analysis +### Valgrind + +## Setting up benchmarks + +## License + +MIT License + + diff --git a/thirdparty/ryml/ext/c4core/cmake/TargetArchitecture.cmake b/thirdparty/ryml/ext/c4core/cmake/TargetArchitecture.cmake new file mode 100644 index 000000000..57baf662c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/TargetArchitecture.cmake @@ -0,0 +1,180 @@ + + +function(c4_get_architecture_defines output_var) + c4_get_target_cpu_architecture(arch) + if("${arch}" STREQUAL "x86_64") + set(defines __x86_64__) + elseif("${arch}" STREQUAL "i386") + set(defines __i386__) + elseif("${arch}" STREQUAL "armv8_64") + set(defines __arm__ __aarch64__) + elseif("${arch}" STREQUAL "armv8") + set(defines __arm__ __ARM_ARCH_8__) + elseif("${arch}" STREQUAL "armv7") + set(defines __arm__ __ARM_ARCH_7__) + elseif("${arch}" STREQUAL "armv6") + set(defines __arm__ __ARM_ARCH_6__) + elseif("${arch}" STREQUAL "armv5") + set(defines __arm__ __ARM_ARCH_5__) + elseif("${arch}" STREQUAL "armv4") + set(defines __arm__ __ARM_ARCH_4T__) + elseif("${arch}" STREQUAL "ia64") + set(defines __ia64__) + elseif("${arch}" STREQUAL "ppc64") + set(defines __ppc64__) + elseif("${arch}" STREQUAL "ia64") + set(defines __ia64__) + elseif("${arch}" STREQUAL "riscv64") + set(defines __riscv64__) + elseif("${arch}" STREQUAL "riscv32") + set(defines __riscv32__) + elseif("${arch}" STREQUAL "s390x") + set(defines __s390x__) + else() + message(FATAL_ERROR "unknown target architecture: ${arch}") + endif() + set(${output_var} ${defines} PARENT_SCOPE) +endfunction() + + +# adapted from https://github.com/axr/solar-cmake/blob/master/TargetArch.cmake +# Set ppc_support to TRUE before including this file or ppc and ppc64 +# will be treated as invalid architectures since they are no longer supported by Apple +function(c4_get_target_cpu_architecture output_var) + # this should be more or less in line with c4core/cpu.hpp + set(archdetect_c_code " +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) + #error cmake_ARCH x86_64 +#elif defined(__i386) || defined(__i386__) || defined(_M_IX86) + #error cmake_ARCH i386 +#elif defined(__arm__) || defined(_M_ARM) \ + || defined(__TARGET_ARCH_ARM) || defined(__aarch64__) || defined(_M_ARM64) + #if defined(__aarch64__) || defined(_M_ARM64) + #error cmake_ARCH armv8_64 + #else + #if defined(__ARM_ARCH_8__) || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8) + #error cmake_ARCH armv8 + #elif defined(__ARM_ARCH_7__) || defined(_ARM_ARCH_7) \ + || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) \ + || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 7) \ + || (defined(_M_ARM) && _M_ARM >= 7) + #error cmake_ARCH armv7 + #elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6M__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 6) + #error cmake_ARCH armv6 + #elif defined(__ARM_ARCH_5TEJ__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5) + #error cmake_ARCH armv5 + #elif defined(__ARM_ARCH_4T__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 4) + #error cmake_ARCH armv4 + #else + #error cmake_ARCH arm + #endif + #endif +#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) + #error cmake_ARCH ia64 +#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \ + || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \ + || defined(_M_MPPC) || defined(_M_PPC) + #if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__) + #error cmake_ARCH ppc64 + #else + #error cmake_ARCH ppc32 + #endif +#elif defined(__riscv) + #if __riscv_xlen == 64 + #error cmake_ARCH riscv64 + #else + #error cmake_ARCH riscv32 + #endif +#elif defined(__s390x__) || defined(__zarch__) + #error cmake_ARCH s390x +#endif +#error cmake_ARCH unknown +") + if(APPLE AND CMAKE_OSX_ARCHITECTURES) + # On OS X we use CMAKE_OSX_ARCHITECTURES *if* it was set + # First let's normalize the order of the values + + # Note that it's not possible to compile PowerPC applications if you are using + # the OS X SDK version 10.6 or later - you'll need 10.4/10.5 for that, so we + # disable it by default + # See this page for more information: + # http://stackoverflow.com/questions/5333490/how-can-we-restore-ppc-ppc64-as-well-as-full-10-4-10-5-sdk-support-to-xcode-4 + + # Architecture defaults to i386 or ppc on OS X 10.5 and earlier, depending on the CPU type detected at runtime. + # On OS X 10.6+ the default is x86_64 if the CPU supports it, i386 otherwise. + + foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES}) + if("${osx_arch}" STREQUAL "ppc" AND ppc_support) + set(osx_arch_ppc TRUE) + elseif("${osx_arch}" STREQUAL "i386") + set(osx_arch_i386 TRUE) + elseif("${osx_arch}" STREQUAL "x86_64") + set(osx_arch_x86_64 TRUE) + elseif("${osx_arch}" STREQUAL "ppc64" AND ppc_support) + set(osx_arch_ppc64 TRUE) + else() + message(FATAL_ERROR "Invalid OS X arch name: ${osx_arch}") + endif() + endforeach() + + # Now add all the architectures in our normalized order + if(osx_arch_ppc) + list(APPEND ARCH ppc) + endif() + + if(osx_arch_i386) + list(APPEND ARCH i386) + endif() + + if(osx_arch_x86_64) + list(APPEND ARCH x86_64) + endif() + + if(osx_arch_ppc64) + list(APPEND ARCH ppc64) + endif() + else() + file(WRITE "${CMAKE_BINARY_DIR}/detect_cpu_arch.c" "${archdetect_c_code}") + + enable_language(C) + + # Detect the architecture in a rather creative way... + # This compiles a small C program which is a series of ifdefs that selects a + # particular #error preprocessor directive whose message string contains the + # target architecture. The program will always fail to compile (both because + # file is not a valid C program, and obviously because of the presence of the + # #error preprocessor directives... but by exploiting the preprocessor in this + # way, we can detect the correct target architecture even when cross-compiling, + # since the program itself never needs to be run (only the compiler/preprocessor) + try_run( + run_result_unused + compile_result_unused + "${CMAKE_BINARY_DIR}" + "${CMAKE_BINARY_DIR}/detect_cpu_arch.c" + COMPILE_OUTPUT_VARIABLE ARCH + CMAKE_FLAGS CMAKE_OSX_ARCHITECTURES=${CMAKE_OSX_ARCHITECTURES} + ) + + # Parse the architecture name from the compiler output + string(REGEX MATCH "cmake_ARCH ([a-zA-Z0-9_]+)" ARCH "${ARCH}") + + # Get rid of the value marker leaving just the architecture name + string(REPLACE "cmake_ARCH " "" ARCH "${ARCH}") + + # If we are compiling with an unknown architecture this variable should + # already be set to "unknown" but in the case that it's empty (i.e. due + # to a typo in the code), then set it to unknown + if (NOT ARCH) + set(ARCH unknown) + endif() + endif() + + set(${output_var} "${ARCH}" PARENT_SCOPE) +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake b/thirdparty/ryml/ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake new file mode 100644 index 000000000..86d39dae1 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake @@ -0,0 +1,29 @@ +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_SYSTEM_PROCESSOR arm) +SET(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_CROSSCOMPILING TRUE) + +find_program(CC_GCC arm-linux-gnueabihf-gcc REQUIRED) + +set(CMAKE_FIND_ROOT_PATH /usr/arm-gnueabihf) + +# Cross compiler +SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) +SET(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) +set(CMAKE_LIBRARY_ARCHITECTURE arm-linux-gnueabihf) + +# Search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# Libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +set(THREADS_PTHREAD_ARG "0" CACHE STRING "Result from TRY_RUN" FORCE) + +get_filename_component(TOOLCHAIN_DIR "${CC_GCC}" DIRECTORY) +get_filename_component(TOOLCHAIN_DIR "${TOOLCHAIN_DIR}" DIRECTORY) +set(TOOLCHAIN_SO_DIR "${TOOLCHAIN_DIR}/arm-linux-gnueabihf/") +#/home/jpmag/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf +set(CMAKE_CROSSCOMPILING_EMULATOR qemu-arm -L ${TOOLCHAIN_SO_DIR}) diff --git a/thirdparty/ryml/ext/c4core/cmake/Toolchain-Armv7.cmake b/thirdparty/ryml/ext/c4core/cmake/Toolchain-Armv7.cmake new file mode 100644 index 000000000..7f6867126 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Toolchain-Armv7.cmake @@ -0,0 +1,84 @@ +# taken from https://stackoverflow.com/a/49086560 + +# tested with the toolchain from ARM: +# gcc-arm-9.2-2019.12-mingw-w64-i686-arm-none-linux-gnueabihf.tar.xz +# found at +# https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-a/downloads + +# see also: +# https://stackoverflow.com/questions/42371788/how-to-run-helloworld-on-arm +# https://dev.to/younup/cmake-on-stm32-the-beginning-3766 + +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_SYSTEM_PROCESSOR arm) +SET(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_CROSSCOMPILING TRUE) + +find_program(CC_GCC arm-none-linux-gnueabihf-gcc REQUIRED) + +set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf) + +# Cross compiler +SET(CMAKE_C_COMPILER arm-none-linux-gnueabihf-gcc) +SET(CMAKE_CXX_COMPILER arm-none-linux-gnueabihf-g++) +set(CMAKE_LIBRARY_ARCHITECTURE arm-none-linux-gnueabihf) + +# Search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# Libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +set(THREADS_PTHREAD_ARG "0" CACHE STRING "Result from TRY_RUN" FORCE) + +get_filename_component(TOOLCHAIN_DIR "${CC_GCC}" DIRECTORY) +get_filename_component(TOOLCHAIN_DIR "${TOOLCHAIN_DIR}" DIRECTORY) +set(TOOLCHAIN_SO_DIR "${TOOLCHAIN_DIR}/arm-none-linux-gnueabihf/libc/") + +#/home/jpmag/local/arm/gcc-arm-9.2-2019.12-x86_64-arm-none-linux-gnueabihf +set(CMAKE_CROSSCOMPILING_EMULATOR qemu-arm -L ${TOOLCHAIN_SO_DIR}) + +return() + + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_CROSSCOMPILING 1) + +set(CMAKE_C_COMPILER "arm-none-eabi-gcc") +set(CMAKE_CXX_COMPILER "arm-none-eabi-g++") + + +set(CMAKE_FIND_ROOT_PATH /usr/arm-none-eabi) +set(CMAKE_EXE_LINKER_FLAGS "--specs=nosys.specs" CACHE INTERNAL "") + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +set(COMPILER_FLAGS "-marm -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a9 -D_GNU_SOURCE") + +message(STATUS) +message(STATUS) +message(STATUS) + +if(NOT DEFINED CMAKE_C_FLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILER_FLAGS}" CACHE STRING "") +else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILER_FLAGS}") +endif() + +message(STATUS) +message(STATUS) +message(STATUS) +message(STATUS) + +if(NOT DEFINED CMAKE_CXX_FLAGS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_FLAGS}" CACHE STRING "") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILER_FLAGS}") +endif() diff --git a/thirdparty/ryml/ext/c4core/cmake/Toolchain-PS4.cmake b/thirdparty/ryml/ext/c4core/cmake/Toolchain-PS4.cmake new file mode 100644 index 000000000..260b9b078 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Toolchain-PS4.cmake @@ -0,0 +1,73 @@ +# Copyright 2017 Autodesk Inc. http://www.autodesk.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You may +# obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +# This module is shared; use include blocker. +if( _PS4_TOOLCHAIN_ ) + return() +endif() +set(_PS4_TOOLCHAIN_ 1) + +# PS4 SCE version requirement +set(REQUIRED_PS4_VERSION "4.000") + +# Get PS4 SCE environment +if( EXISTS "$ENV{SCE_ROOT_DIR}" AND IS_DIRECTORY "$ENV{SCE_ROOT_DIR}" ) + string(REGEX REPLACE "\\\\" "/" PS4_ROOT $ENV{SCE_ROOT_DIR}) + string(REGEX REPLACE "//" "/" PS4_ROOT ${PS4_ROOT}) + if( EXISTS "$ENV{SCE_ORBIS_SDK_DIR}" AND IS_DIRECTORY "$ENV{SCE_ORBIS_SDK_DIR}" ) + string(REGEX REPLACE "\\\\" "/" PS4_SDK $ENV{SCE_ORBIS_SDK_DIR}) + string(REGEX REPLACE "//" "/" PS4_SDK ${PS4_SDK}) + get_filename_component(SCE_VERSION "${PS4_SDK}" NAME) + endif() +endif() + +# Report and check version if it exist +if( NOT "${SCE_VERSION}" STREQUAL "" ) + message(STATUS "PS4 SCE version found: ${SCE_VERSION}") + if( NOT "${SCE_VERSION}" MATCHES "${REQUIRED_PS4_VERSION}+" ) + message(WARNING "Expected PS4 SCE version: ${REQUIRED_PS4_VERSION}") + if( PLATFORM_TOOLCHAIN_ENVIRONMENT_ONLY ) + set(PS4_ROOT) + set(PS4_SDK) + endif() + endif() +endif() + +# If we only want the environment values, exit now +if( PLATFORM_TOOLCHAIN_ENVIRONMENT_ONLY ) + return() +endif() + +# We are building PS4 platform, fail if PS4 SCE not found +if( NOT PS4_ROOT OR NOT PS4_SDK ) + message(FATAL_ERROR "Engine requires PS4 SCE SDK to be installed in order to build PS4 platform.") +endif() + +# Tell CMake we are cross-compiling to PS4 (Orbis) +set(CMAKE_SYSTEM_NAME Orbis) +set(PS4 True) + +# Set CMake system root search path +set(CMAKE_SYSROOT "${PS4_ROOT}") + +# Set compilers to the ones found in PS4 SCE SDK directory +set(CMAKE_C_COMPILER "${PS4_SDK}/host_tools/bin/orbis-clang.exe") +set(CMAKE_CXX_COMPILER "${PS4_SDK}/host_tools/bin/orbis-clang++.exe") +set(CMAKE_ASM_COMPILER "${PS4_SDK}/host_tools/bin/orbis-as.exe") + +# Only search the PS4 SCE SDK, not the remainder of the host file system +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/thirdparty/ryml/ext/c4core/cmake/Toolchain-XBoxOne.cmake b/thirdparty/ryml/ext/c4core/cmake/Toolchain-XBoxOne.cmake new file mode 100644 index 000000000..2ed9fad73 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/Toolchain-XBoxOne.cmake @@ -0,0 +1,93 @@ +# Copyright 2017 Autodesk Inc. http://www.autodesk.com +# +# Licensed under the Apache License, Version 2.0 (the "License"); you +# may not use this file except in compliance with the License. You may +# obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +# This module is shared; use include blocker. +if( _XB1_TOOLCHAIN_ ) + return() +endif() +set(_XB1_TOOLCHAIN_ 1) + +# XB1 XDK version requirement +set(REQUIRED_XB1_TOOLCHAIN_VERSION "160305") + +# Get XDK environment +if( EXISTS "$ENV{DurangoXDK}" AND IS_DIRECTORY "$ENV{DurangoXDK}" ) + string(REGEX REPLACE "\\\\" "/" XDK_ROOT $ENV{DurangoXDK}) + string(REGEX REPLACE "//" "/" XDK_ROOT ${XDK_ROOT}) +endif() + +# Fail if XDK not found +if( NOT XDK_ROOT ) + if( PLATFORM_TOOLCHAIN_ENVIRONMENT_ONLY ) + return() + endif() + message(FATAL_ERROR "Engine requires XB1 XDK to be installed in order to build XB1 platform.") +endif() + +# Get toolchain version +get_filename_component(XDK_TOOLCHAIN_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Durango XDK\\${REQUIRED_XB1_TOOLCHAIN_VERSION};EditionVersion]" NAME) + +if( XDK_TOOLCHAIN_VERSION STREQUAL REQUIRED_XB1_TOOLCHAIN_VERSION ) + message(STATUS "Found required XDK toolchain version (${XDK_TOOLCHAIN_VERSION})") +else() + get_filename_component(XDK_TOOLCHAIN_VERSION "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Durango XDK;Latest]" NAME) + message(WARNING "Could not find required XDK toolchain version (${REQUIRED_XB1_TOOLCHAIN_VERSION}), using latest version instead (${XDK_TOOLCHAIN_VERSION})") +endif() + +# If we only want the environment values, exit now +if( PLATFORM_TOOLCHAIN_ENVIRONMENT_ONLY ) + return() +endif() + +# Find XDK compiler directory +if( CMAKE_GENERATOR STREQUAL "Visual Studio 11 2012" ) + set(XDK_COMPILER_DIR "${XDK_ROOT}/${XDK_TOOLCHAIN_VERSION}/Compilers/dev11.1") +elseif( CMAKE_GENERATOR STREQUAL "Visual Studio 14 2015" ) + get_filename_component(XDK_COMPILER_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\14.0_Config\\Setup\\VC;ProductDir]" DIRECTORY) + if( DEFINED XDK_COMPILER_DIR ) + string(REGEX REPLACE "\\\\" "/" XDK_COMPILER_DIR ${XDK_COMPILER_DIR}) + string(REGEX REPLACE "//" "/" XDK_COMPILER_DIR ${XDK_COMPILER_DIR}) + endif() + if( NOT XDK_COMPILER_DIR ) + message(FATAL_ERROR "Can't find Visual Studio 2015 installation path.") + endif() +else() + message(FATAL_ERROR "Unsupported Visual Studio version!") +endif() + +# Tell CMake we are cross-compiling to XBoxOne (Durango) +set(CMAKE_SYSTEM_NAME Durango) +set(XBOXONE True) + +# Set CMake system root search path +set(CMAKE_SYSROOT "${XDK_COMPILER_DIR}") + +# Set the compilers to the ones found in XboxOne XDK directory +set(CMAKE_C_COMPILER "${XDK_COMPILER_DIR}/vc/bin/amd64/cl.exe") +set(CMAKE_CXX_COMPILER "${XDK_COMPILER_DIR}/vc/bin/amd64/cl.exe") +set(CMAKE_ASM_COMPILER "${XDK_COMPILER_DIR}/vc/bin/amd64/ml64.exe") + +# Force compilers to skip detecting compiler ABI info and compile features +set(CMAKE_C_COMPILER_FORCED True) +set(CMAKE_CXX_COMPILER_FORCED True) +set(CMAKE_ASM_COMPILER_FORCED True) + +# Only search the XBoxOne XDK, not the remainder of the host file system +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Global variables +set(XBOXONE_SDK_REFERENCES "Xbox Services API, Version=8.0;Xbox GameChat API, Version=8.0") diff --git a/thirdparty/ryml/ext/c4core/cmake/amalgamate_utils.py b/thirdparty/ryml/ext/c4core/cmake/amalgamate_utils.py new file mode 100644 index 000000000..d4115c974 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/amalgamate_utils.py @@ -0,0 +1,219 @@ +import re +import os + + +class cmtfile: + """commented file""" + def __init__(self, filename): + self.filename = filename + def __str__(self): + return self.filename + + +class cmttext: + """commented text""" + def __init__(self, text): + self.text = text + def __str__(self): + return self.text + + +class ignfile: + """ignore file""" + def __init__(self, filename): + self.filename = filename + def __str__(self): + return self.filename + + +class hdrfile: + """header file, with custom include guard""" + def __init__(self, filename, incpattern, include_guard=None): + self.filename = filename + self.incpattern = incpattern + self.include_guard = include_guard + def __str__(self): + return self.filename + + +class injfile: + """header file, to be injected at the first include point""" + def __init__(self, filename, incpattern): + self.filename = filename + self.incpattern = incpattern + def __str__(self): + return self.filename + + +class injcode: + """direct code to inject""" + def __init__(self, code): + self.code = code + def __str__(self): + return self.code + + +class onlyif: + def __init__(self, condition, obj): + self.condition = condition + self.obj = obj + + +def catfiles(filenames, rootdir, + include_regexes, + definition_macro, + repo, + result_incguard): + file_re = re.compile('[-./]') + sepb = "//" + ("**" * 40) + sepf = "//" + ("--" * 40) + to_inject = {} + custom_include_guards = {} + def banner(s): + return f"\n\n\n{sepb}\n{sepf}\n// {s}\n// {repo}/{s}\n{sepf}\n{sepb}\n\n" + def footer(s): + return f"\n\n// (end {repo}/{s})\n" + def incguard(filename): + return custom_include_guards.get(filename, + f"{file_re.sub('_', filename).upper()}_") + def replace_include(rx, match, line, guard): + line = line.rstrip() + incl = match.group(1) + if to_inject.get(incl) is None: + if guard is None: + guard = incguard(incl) + return f"""// amalgamate: removed include of +// {repo}/src/{incl} +//{line} +#if !defined({guard}) && !defined(_{guard}) +#error "amalgamate: file {incl} must have been included at this point" +#endif /* {guard} */\n +""" + else: + entry = to_inject[incl] + del to_inject[incl] + return append_file(entry.filename) + def append_file(filename, guard=None): + s = "" + with open(filename, encoding="utf8") as f: + for line in f.readlines(): + for rx in include_regexes: + match = rx.match(line) + if match: + line = replace_include(rx, match, line, guard) + s += line + return s + def append_cpp(filename): + return f"""#ifdef {definition_macro} +{append_file(filename)} +#endif /* {definition_macro} */ +""" + def is_src(filename): + return filename.endswith(".cpp") or filename.endswith(".c") + def cmtline(line, more=""): + if len(line.strip()) > 0: + return f"// {line}{more}" + else: + return "//\n" + out = "" + for entry in filenames: + if isinstance(entry, onlyif): + if entry.condition: + entry = entry.obj + else: + continue + if isinstance(entry, ignfile): + pass + elif isinstance(entry, cmttext): + for line in entry.text.split("\n"): + out += cmtline(line, "\n") + elif isinstance(entry, cmtfile): + filename = f"{rootdir}/{entry.filename}" + out += banner(entry.filename) + with open(filename, encoding="utf8") as file: + for line in file.readlines(): + out += cmtline(line) + elif isinstance(entry, injcode): + out += f"\n{entry.code}\n" + elif isinstance(entry, injfile): + entry.filename = f"{rootdir}/{entry.filename}" + to_inject[entry.incpattern] = entry + else: + filename = f"{rootdir}/{entry}" + out += banner(entry) + if isinstance(entry, hdrfile): + if entry.include_guard is not None: + custom_include_guards[entry.incpattern] = entry.include_guard + out += append_file(filename, entry.include_guard) + else: + assert isinstance(entry, str) + if is_src(filename): + out += append_cpp(filename) + else: + out += append_file(filename) + out += footer(entry) + return f"""#ifndef {result_incguard} +#define {result_incguard} + +{out} +#endif /* {result_incguard} */ +""" + +def include_only_first(file_contents: str): + rx = [ + re.compile(r'^\s*#\s*include "(.*?)".*'), + re.compile(r'^\s*#\s*include <(.*?)>.*'), + ] + already_included = {} + out = "" + for line in file_contents.split("\n"): + for expr in rx: + match = expr.match(line) + if match: + incl = match.group(1) + if already_included.get(incl) is None: + already_included[incl] = line + if incl.endswith(".h"): + cpp_version = f"c{incl[:-2]}" + already_included[cpp_version] = line + elif incl.startswith("c") and not (incl.endswith(".h") or incl.endswith(".hpp")): + c_version = f"{incl[1:]}.h" + already_included[c_version] = line + else: + line = f"//included above:\n//{line}" + break + out += line + out += "\n" + return out + + +def mkparser(**bool_args): + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("output", default=None, nargs='?', help="output file. defaults to stdout") + for k, (default, help) in bool_args.items(): + # https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse + feature = parser.add_mutually_exclusive_group(required=False) + yes = '--' + k + no = '--no-' + k + if default: + yes_default = "this is the default" + no_default = f"the default is {yes}" + else: + yes_default = f"the default is {no}" + no_default = "this is the default" + feature.add_argument(yes, dest=k, action='store_true', help=f"{help}. {yes_default}.") + feature.add_argument(no, dest=k, action='store_false', help=f"{help}. {no_default}.") + parser.set_defaults(**{k: default}) + return parser + + +def file_put_contents(filename: str, contents: str): + if filename is None: + print(contents) + else: + dirname = os.path.dirname(filename) + if dirname: + os.makedirs(dirname, exist_ok=True) + with open(filename, "w", encoding="utf8") as output: + output.write(contents) diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/.gitignore b/thirdparty/ryml/ext/c4core/cmake/bm-xp/.gitignore new file mode 100644 index 000000000..ccf5d016e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/.gitignore @@ -0,0 +1 @@ +static/*
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/README.md b/thirdparty/ryml/ext/c4core/cmake/bm-xp/README.md new file mode 100644 index 000000000..485cd4edb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/README.md @@ -0,0 +1,7 @@ +# Benchmark explorer + +You need to start a http server on this folder: + +```shellsession +$ python bm.py serve . +``` diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm.js b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm.js new file mode 100644 index 000000000..402d6120f --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm.js @@ -0,0 +1,475 @@ + +/* https://stackoverflow.com/questions/9050345/selecting-last-element-in-javascript-array */ +function last(arr) +{ + return arr[arr.length - 1]; +}; + +function dbg() +{ + /* pass ?dbg=1 to enable debug logs */ + /*if(!getParam('dbg', 0)){ + return; + }*/ + elm = $("#dbg"); + var s = ""; + for (var i = 0; i < arguments.length; i++) { + if(i > 0) s += ' '; + s += arguments[i].toString(); + } + console.log(s); + s+= "\n"; + elm.append(document.createTextNode(s)); +} + + +function iterArr(arr, fn) { + for (var key in arr) { + if (arr.hasOwnProperty(key)) { + fn(key, arr[key]); + } + } +} + + +function fileContents(file, onComplete) +{ + dbg(`${file}: requesting...`); + var data; + $.get(file, function(d) { + dbg(`${file}: got response! ${d.length}B...`); + if(onComplete) { + onComplete(d); + } + }, "text"); +} + + + +/* https://stackoverflow.com/questions/7394748/whats-the-right-way-to-decode-a-string-that-has-special-html-entities-in-it/7394787 */ +function decodeHtmlEntities(str) +{ + return str + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace(""", "\"") + .replace(/&#(\d+);/g, function(match, dec) { + return String.fromCharCode(dec); + }); +} +/* https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript */ +function escapeHtml(unsafe) +{ + return unsafe + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + + +/* URL params ----------------------------------------------------------------- */ + +var _curr_url_params = null; +function parseUrlParams() +{ + var keyvals = []; + var keys = document.location.search.substring(1).split('&'); + dbg("keys=", keys) + for(var i = 0; i < keys.length; i++) { + var key = keys[i].split('='); + dbg("i=", i, " key=", key); + keyvals.push(key[0]); + keyvals[key[0]] = key[1]; + } + _curr_url_params = keyvals; +} + +function dbgParams() { + iterArr(_curr_url_params, function(key, val){ dbg("url params:", key, "=", val); }) + +} +function getParam(name, fallback) +{ + if(_curr_url_params === null) { parseUrlParams(); } + if(name in _curr_url_params) { + return _curr_url_params[name]; + } + return fallback; +} + +function setParam(name, value) { + if(_curr_url_params === null) { parseUrlParams(); } + _curr_url_params[name] = value; + // https://stackoverflow.com/questions/486896/adding-a-parameter-to-the-url-with-javascript + document.location.search = joinParams(); +} + +function joinParams() { + if(_curr_url_params === null) { parseUrlParams(); } + var s = ""; + iterArr(_curr_url_params, function(key, val){ + if(s != ""){ s += '&'; } + s += `${key}=${val}`; + }); + return s; +} + + +/* ----------------------------------------------------------------------------- */ + +function colMax(data, col) +{ + var max = -1.e30; + data.forEach(function(item, index){ + max = item[col] > max ? item[col] : max; + }); + return max; +} + +function colMin(data, col) +{ + var min = 1.e30; + data.forEach(function(item, index){ + min = item[col] < min ? item[col] : min; + }); + return min; +} + +/* https://stackoverflow.com/questions/2283566/how-can-i-round-a-number-in-javascript-tofixed-returns-a-string */ +function toFixedNumber(num, digits, base) +{ + var pow = Math.pow(base||10, digits); + return Math.round(num*pow) / pow; +} + +function humanReadable(sz, base=1024, precision=3) +{ + var i = -1; + var units; + if(base == 1000) + { + units = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']; + } + else if(base == 1024) + { + units = ['ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; + } + do + { + sz /= base; + i++; + } while (sz > base); + return sz.toFixed(precision) + units[i]; +}; + + +/* ----------------------------------------------------------------------------- */ + +class BmResults +{ + constructor(dict={}) + { + Object.assign(this, dict); + for(var i = 0; i < this.benchmarks.length; ++i) { + var bm = this.benchmarks[i]; + bm.name = decodeHtmlEntities(bm.name); + bm.run_name = decodeHtmlEntities(bm.run_name); + } + } +} + +var bmSpecs; +function iterBms(fn) +{ + iterArr(bmSpecs.bm, fn); +} + +function loadSpecs(specs) +{ + dbg("loading specs ...."); + iterArr(specs, function(k, v){dbg("k=", k, 'v=', v); }); + $("#heading-title").html(`Benchmarks: <a href="${specs.url}">${specs.projname}</a>`); + bmSpecs = specs; + var toc = $("#toc"); + /*toc.append(`<li><a href="#" onclick="setParam('bm', 'all');">Load all</a></li>`);*/ + iterBms(function(key, bm) { + toc.append(`<li><a href="#${key}" onclick="setParam('bm', '${key}');">${key}</a>: ${bm.specs.desc}</li>`) + bm.name = key; + }); + // load if required + currBm = getParam("bm", ""); + dbg("params=", _curr_url_params, currBm); + if(currBm != "") { + dbg("loading BM from URL:", currBm) + loadBm(currBm); + } +} + +function normalizeBy(results, column_name, best_fn) +{ + var best = best_fn(results.benchmarks, column_name); + results.benchmarks.forEach(function(item, index){ + item[`${column_name}_normalized`] = item[column_name] / best; + }); +} + + +function loadAll() +{ + var id = "#bm-results"; + $(id).empty(); + var i = 0; + iterBms(function(key, bm){ + if(i++ > 0) $(id).append("<div class='bm-sep'><hr/></div>"); + appendBm(key); + }); +} + + +function loadBm(key) +{ + dbg("loading-.....", key); + /*if(key == "all") { + loadAll(); + }*/ + $("#bm-results").empty(); + var bm = bmSpecs.bm[key]; + if(bm.src != "") { + fileContents(bm.src, function(data){ + dbg(`${key}: got src data!`) + bm.src_data = data; + }); + } + var latestRun = last(bm.entries); + var bmfile = `${latestRun}/${key}.json`; + dbg("bmfile=", bmfile); + fileContents("bm/"+bmfile, function(data){ + dbg(`${key}: got bm data!`) + bm.results_data = new BmResults(JSON.parse(data)); + bm.results_data.benchmarks.forEach(function(item, index){ + item.id = index; + }); + normalizeBy(bm.results_data, 'iterations', colMin); + normalizeBy(bm.results_data, 'real_time', colMin, ); + normalizeBy(bm.results_data, 'cpu_time', colMin); + normalizeBy(bm.results_data, 'bytes_per_second', colMin); + normalizeBy(bm.results_data, 'items_per_second', colMin); + appendBm(latestRun, key, bm); + }); +} + + +function appendBm(run_id, id, bm) +{ + if($(document).find(`bm-results-${id}`).length == 0) + { + $("#bm-results").append(` +<div id="bm-results-${id}"> + <h2 id="bm-title-${id}">${id}</h2> + + <h3 id="heading-details-table-${id}">Run details</h3><table id="table-details-${id}" class="datatable" width="800px"></table> + + <h3 id="heading-table-${id}">Result tables</h3> + <h4 id="heading-table-${id}_pretty">Results</h4><table id="table-${id}_pretty" class="datatable" width="800px"></table> + <h4 id="heading-table-${id}_normalized">Normalized by column min</h4><table id="table-${id}_normalized" class="datatable" width="800px"></table> + + <h3 id="heading-chart-${id}">Chart</h2> + <div id="chart-container-${id}"></div> + + <h3 id="heading-code-${id}">Code</h2> + <pre><code id="code-${id}" class="lang-c++"></code></pre> +</div> +`); + } + var results = bm.results_data; + var code = bm.src_data; + loadDetailsTable(run_id, id, bm, results); + loadTable(id, bm, results); + loadChart(id, bm, results); + loadCode(id, bm, code); +} + + +function loadCode(elmId, bm, code) +{ + var elm = $(`#code-${elmId}`); + elm.text(code); + /* hljs.highlightBlock(elm); // this doesn't work */ + /* ... and this is very inefficient: */ + document.querySelectorAll('pre code').forEach((block) => { + hljs.highlightBlock(block); + }); +} + +function parseRunId(run_id) +{ + // example: + // commit id / cpu id - system id - build id + // git20201204_202919-b3f7fa7/x86_64_b9db3176-linux_4e9326b4-64bit_Debug_gcc10.2.0_10c5d03c + // git20201203_193348-2974fb0/x86_64_16ac0500-win32_59f3579c-64bit_MinSizeRel_msvc19.28.29304.1_32f6fc66 + // to tune the regex: https://regex101.com/r/rdkPi8/1 + // commit / cpu - system - build + var rx = /^(.+?)-([0-9a-f]{7})\/(.+?)_([0-9a-f]{8})-(.+?)_([0-9a-f]{8})-(.+?)_([0-9a-f]{8})$/gim; + var tag = rx.exec(run_id); + dbg("fdx: run_id=", run_id); + dbg("fdx: tag=", tag); + dbg("fdx: len=", tag.length); + return { + commit_id: `${tag[2]}: ${tag[1]}`, + cpu_id: `${tag[4]}: ${tag[3]} `, + system_id: `${tag[6]}: ${tag[5]}`, + build_id: `${tag[8]}: ${tag[7]}`, + }; +} + +function getBuildId(run_id) +{ + return parseRunId(run_id).build_id; +} + +function loadDetailsTable(run_id, id, bm, results) +{ + var url = bmSpecs.url; + var run = bmSpecs.runs[run_id]; + var commit = bmSpecs.commit[run.commit].specs; + var cpu = bmSpecs.cpu[run.cpu].specs; + var system = bmSpecs.system[run.system].specs; + + let other_commit_entries = bmSpecs.commit[run.commit].entries.filter( + entry_run => entry_run != run_id + ).map(entry_run => getBuildId(entry_run)).join('<br>'); + + /* https://datatables.net/ */ + $(`#table-details-${id}`).DataTable({ + data: results.benchmarks, + info: false, + paging: false, + searching: false, + retrieve: false, + order: [], + columns: [ + {title: "", data: "desc"}, + {title: "", data: "contents"}, + ], + data: [ + {desc: "benchmark id" , contents: id}, + {desc: "commit" , contents: ahref(`${url}/commit/${commit.sha1}`, commit.sha1)}, + {desc: "commit date" , contents: ahref(`${url}/commit/${commit.sha1}`, commit.committed_datetime)}, + {desc: "commit summary", contents: ahref(`${url}/commit/${commit.sha1}`, commit.summary)}, + {desc: "source tree" , contents: ahref(`${url}/tree/${commit.sha1}`, `tree @ ${commit.sha1}`)}, + {desc: "benchmark" , contents: ahref(`${url}/tree/${commit.sha1}/${bm.specs.src}`, `source @ ${commit.sha1}`)}, + {desc: "cpu used" , contents: `${cpu.arch} ${cpu.brand_raw}`}, + {desc: "system used" , contents: `${system.uname.system} ${system.uname.release}`}, + {desc: "this build" , contents: `<pre>${getBuildId(run_id)}</pre>`}, + {desc: "commit builds" , contents: `<pre>${other_commit_entries}</pre>`}, + ] + }); + function ahref(url, txt) { return `<a href="${url}" target="_blank">${txt}</a>`; } +} + + +function loadTable(id, bm, results) +{ + function render_int(data, type, row, meta) { return toFixedNumber(data, 0); } + function render_megas(data, type, row, meta) { return toFixedNumber(data / 1.e6, 3); } + function render_fixed(data, type, row, meta) { return toFixedNumber(data, 3); } + function render_human(data, type, row, meta) { return humanReadable(data, 1000, 3); } + + addTable("_pretty" , "" , {ns: render_int, iters: render_megas, rates: render_megas}); + addTable("_normalized", "_normalized", {ns: render_fixed, iters: render_fixed, rates: render_fixed}); + + function addTable(suffix, data_suffix, renderers) { + /* https://datatables.net/ */ + var searching = (results.benchmarks.count > 20); + var ratePrefix = renderers.rates == render_megas ? "M" : ""; + var iterPrefix = renderers.iters == render_megas ? "M" : ""; + var clockSuffix = data_suffix == "_normalized" ? "" : "(ns)"; + $(`#table-${id}${suffix}`).DataTable( { + data: results.benchmarks, + info: false, + paging: false, + searching: searching, + retrieve: searching, + /* https://datatables.net/reference/option/columns.type */ + columns: [ + {title: "ID", data: "id", type: "num"}, + {title: "Name", data: "name", render: function(data, type, row, meta) { return escapeHtml(data); }}, + {title: `${ratePrefix}B/s` , data: `bytes_per_second${data_suffix}`, type: "num", className: "text-right", render: renderers.rates}, + {title: `${ratePrefix}items/s` , data: `items_per_second${data_suffix}`, type: "num", className: "text-right", render: renderers.rates}, + {title: `Clock${clockSuffix}` , data: `real_time${data_suffix}` , type: "num", className: "text-right", render: renderers.ns}, + {title: `CPU${clockSuffix}` , data: `cpu_time${data_suffix}` , type: "num", className: "text-right", render: renderers.ns}, + {title: `${ratePrefix}Iterations`, data: `iterations${data_suffix}` , type: "num", className: "text-right", render: renderers.iters}, + ]}); + } +} + +function loadChart(id, bm, results) +{ + + addChartFromColumn('bytes_per_second_normalized', "B/s", "(more is better)"); + addChartFromColumn('items_per_second_normalized', "items/s", "(more is better)"); + addChartFromColumn('iterations_normalized', "Iterations", "(more is better)"); + addChartFromColumn('real_time_normalized', "Clock time", "(less is better)"); + addChartFromColumn('cpu_time_normalized', "CPU time", "(less is better)"); + + function addChartFromColumn(column, column_name, obs) { + var elmId = `chart-${id}-${column}`; + var canvas = `${elmId}-canvas`; + + $(`#chart-container-${id}`).append(` +<div id="${elmId}" class="chart"> + <canvas id="${canvas}"></canvas> +</div> +`); + + var chart = new CanvasJS.Chart(elmId, { + animationEnabled: false, + title:{ + fontSize: 24, + /* text: `${id}: ${column_name}\n${obs}` */ + text: `${column_name}\n${obs}` + }, + axisX: { + labelFontSize: 12, + }, + data: [{ + type: "bar", + axisYType: "secondary", + color: "#eb7434",/*"#014D65",*/ + dataPoints: results.benchmarks.map(function(item){ + return { + indexLabelFormatter: function(e) { return e.dataPoint.indexLabel; }, + indexLabelFontSize: 16, + indexLabel: item.name, + /* label: item.name, */ + y: item[column], + /* save the result here: the tooltip will show the full thing */ + benchmark_results: item + }; + }), + }], + toolTip: { + /*content: "{indexLabel}: {y}",*/ + contentFormatter: function(e){ + function hr(val) { return humanReadable(val, 1000, 3); } + function fx(val) { return toFixedNumber(val, 3); } + function fxi(val) { return toFixedNumber(val, 0); } + function getRow(name, abs, rel) { return `<tr><td>${name}</td><td>${abs}</td><td>${rel}x min</td></tr>`; } + var r = e.entries[0].dataPoint.benchmark_results; + var hdrRow = `<tr><th></th><th>Absolute</th><th>Normalized</th></tr>`; + var bpsRow = getRow("B/s", hr(r.bytes_per_second), fx(r.bytes_per_second_normalized)); + var ipsRow = getRow("items/s", hr(r.items_per_second), fx(r.items_per_second_normalized)); + var cpuRow = getRow("CPU", fxi(r.cpu_time) + "ns", fx(r.cpu_time_normalized)); + var clockRow = getRow("Clock", fxi(r.real_time) + "ns", fx(r.real_time_normalized)); + var itersRow = getRow("Iterations", hr(r.iterations), fx(r.iterations_normalized)); + var table = `<table>${hdrRow}${bpsRow}${ipsRow}${cpuRow}${clockRow}${itersRow}</table>`; + return `<h4>${escapeHtml(r.name)}</h4>${table}`; + } + } + }); + chart.render(); + } +} diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_plot.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_plot.py new file mode 100644 index 000000000..3bcab5f4d --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_plot.py @@ -0,0 +1,746 @@ +import os +import sys +import copy +import re +import itertools +import typing +import enum + +# https://stackoverflow.com/questions/11351032/named-tuple-and-default-values-for-optional-keyword-arguments +from dataclasses import dataclass + +from munch import Munch, munchify + +import bokeh.io as bki +import bokeh.models as bkm +import bokeh.plotting as bkp +import bokeh.transform as bkt +import bokeh.layouts as bkl +from bokeh.models.markers import marker_types as bk_markers +# https://docs.bokeh.org/en/latest/docs/reference/palettes.html +from bokeh.palettes import d3 as bk_palette_d3 +bk_palette = bk_palette_d3['Category20c'][20] + +# saving bokeh to png is not working, so we save png using matplotlib +import matplotlib.pyplot as plt +import matplotlib.ticker as plttck +plt_markers = [c for c in ".,ov^<>1234spP*hH+xXDdl"] + +from bm_util import _enum +from bm_util import * +from bm_run import BenchmarkRun, BenchmarkPanel + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + + +# https://stackoverflow.com/questions/11351032/named-tuple-and-default-values-for-optional-keyword-arguments +@dataclass +class BarChartSpecs: + horizontal: bool = True + bar_width: float = 0.9 + + +@dataclass +class LineChartSpecs: + width: int = 1000 + xlog: bool = False + ylog: bool = False + xlabel: str = "" + ylabel: str = "" + + +def _plt_save_png(name): + log(name) + plt.savefig(name, bbox_inches='tight', dpi=100) + + +def _plt_clear(): + plt.clf() + + +def _bokeh_save_html(name, p): + log(name) + bki.save(p, name) + + +def _bokeh_adjust_figure_props(p): + p.toolbar.autohide = True + #p.toolbar.active_inspect = [hover_tool, crosshair_tool] + p.toolbar.active_drag = "auto" + p.toolbar.active_scroll = "auto" + p.legend + p.legend.click_policy = "hide" + p.legend.label_text_font_size = "10px" + + +def bokeh_plot_many(plots, name: str, ncols: int = 2): + layout = bkl.gridplot(plots, ncols=ncols) + _bokeh_save_html(name, layout) + #bkp.show(layout) + + +def plot_benchmark_run_as_bars(bm: BenchmarkRun, title: str, + bar_names, bar_values, bar_label, + **kwargs): + kwargs = BarChartSpecs(**kwargs) + # + palette = itertools.cycle(bk_palette) + colors = [next(palette) for _ in bar_names] + # + fig_args_bokeh = { + "title": title, + #"toolbar_location": None, + #"tools": "" + } + if kwargs.horizontal: + # + # plot with bokeh (interactive, but cannot export png) + rnames = list(reversed(bar_names)) + rvalues = list(reversed(bar_values)) + rcolors = list(reversed(colors)) + p = bkp.figure(y_range=rnames, **fig_args_bokeh) + p.hbar(y=rnames, right=rvalues, fill_color=rcolors, + line_color=rcolors, height=kwargs.bar_width) + p.ygrid.grid_line_color = None + p.x_range.start = 0 + p.xaxis.axis_label = bar_label + # + # plot with matplotlib (to export png) + p_ = plt.barh(y=rnames, width=rvalues, color=rcolors, + height=kwargs.bar_width) + plt.gca().xaxis.grid(True) + plt.gca().xaxis.set_minor_locator(plttck.AutoMinorLocator()) + plt.xlabel(bar_label, fontsize='small') + plt.yticks(fontsize='x-small') + plt.title(title) + else: + # + # plot with bokeh (interactive, but cannot export png) + p = bkp.figure(x_range=bar_names, **fig_args_bokeh) + p.vbar(x=bar_names, top=bar_values, fill_color=colors, + line_color=colors, width=kwargs.bar_width) + p.xaxis.major_label_orientation = 1 + p.xgrid.grid_line_color = None + p.y_range.start = 0 + p.yaxis.axis_label = bar_label + # + # plot with matplotlib (to export png) + p_ = plt.bar(x=bar_names, height=bar_values, color=colors, + width=kwargs.bar_width) + plt.gca().yaxis.grid(True) + plt.gca().yaxis.set_minor_locator(plttck.AutoMinorLocator()) + plt.ylabel(bar_label, fontsize='small') + plt.xticks(fontsize='x-small') + plt.title(title) + _bokeh_adjust_figure_props(p) + return p, p_ + + +def plot_benchmark_panel_as_lines(bm_panel: BenchmarkPanel, title: str, + xget, yget, nameget, + **kwargs): + kwargs = LineChartSpecs(**kwargs) + # + colors = itertools.cycle(bk_palette) + markers = itertools.cycle(bk_markers) + markers_ = itertools.cycle(plt_markers) + # + # plot with bokeh (interactive, but cannot export png) + p = bkp.figure(title=title, + x_axis_type="log" if kwargs.xlog else "linear", + y_axis_type="log" if kwargs.ylog else "linear", + #background_fill_color="#fafafa", + x_axis_label=kwargs.xlabel, + y_axis_label=kwargs.ylabel, + plot_width=kwargs.width, + ) + # plot with matplotlib (to export png) + plt.title(title) + for bm in bm_panel.runs: + x = xget(bm) + y = yget(bm) + line_name = nameget(bm) + color = next(colors) + marker = next(markers) + marker_ = next(markers_) + # plot with bokeh (interactive, but cannot export png) + #legends.append(LegendItem(name=c, label=line_name)) + p.scatter(x, y, marker=marker, size=8, color=color, + legend_label=line_name) + p.line(x, y, color=color, alpha=0.9, + #muted_color=c, muted_alpha=0.05, + legend_label=line_name) + # + # plot with matplotlib (to export png) + plt.plot(x, y, f'-{marker_}', color=color, label=line_name) + plt.gca().xaxis.grid(True) + plt.gca().yaxis.grid(True) + plt.xscale("log" if kwargs.xlog else "linear") + plt.yscale("log" if kwargs.ylog else "linear") + plt.xlabel(kwargs.xlabel, fontsize='small') + plt.ylabel(kwargs.ylabel, fontsize='small') + plt.gca().legend(loc='center left', bbox_to_anchor=(1, 0.5), fontsize='x-small') + _bokeh_adjust_figure_props(p) + return p + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +####### old code: to remove and tidy up + +@dataclass +class CharconvMeta: # also for atox + title: str + subject: str + function: str + data_type: FundamentalTypes + + @classmethod + def make(cls, bm_title: str): + # eg: + # xtoa_c4_write_dec<uint8_t> + # xtoa_c4_utoa<uint8_t> + # xtoa_c4_xtoa<uint8_t> + # xtoa_c4_to_chars<uint8_t> + # xtoa_std_to_chars<uint8_t> + # xtoa_std_to_string<uint8_t> + # xtoa_sprintf<uint8_t> + # xtoa_sstream_reuse<uint8_t> + # xtoa_sstream<uint8_t> + rx = re.compile(r'(atox|xtoa|xtoahex|xtoaoct|xtoabin)_(.*?)<(u?int\d+_t)>') + if not rx.fullmatch(bm_title): + raise Exception(f"cannot understand bm title: {bm_title}") + subject = rx.sub(r'\1', bm_title) + function = rx.sub(r'\2', bm_title) + data_type = rx.sub(r'\3', bm_title) + return cls( + title=bm_title, + subject=subject, + function=function.replace("c4_", "c4::").replace("std_", "std::"), + data_type=FundamentalTypes.make(data_type) + ) + + def checkbox_groups(self): + return { + 'data_type': [t for t in FundamentalTypes], + } + + @property + def shortname(self): + return self.function + + @property + def shortparams(self): + return str(self.data_type.short) + + @property + def shorttitle(self): + return f"{self.shortname}<{self.shortparams}>" + + +@dataclass +class CharconvThreadsMeta: + function: str + num_threads: int + + @classmethod + def make(cls, bm_title: str): + # eg: + # c4_itoa/real_time/threads:4 + rx = re.compile(r'(.*?)/real_time/threads:(\d+)') + if not rx.fullmatch(bm_title): + raise Exception(f"cannot understand bm title: {bm_title}") + function = rx.sub(r'\1', bm_title) + num_threads = int(rx.sub(r'\2', bm_title)) + return cls( + function=function.replace("c4_", "c4::").replace("std_", "std::"), + num_threads=num_threads + ) + + def checkbox_groups(self): + return {} + + @property + def shortname(self): + return self.function + + @property + def shorttitle(self): + return self.shortname + + +def plot_charconv_bars(bm_panel: BenchmarkPanel, panel_title_human: str, outputfile_prefix: str): + assert os.path.isabs(outputfile_prefix), outputfile_prefix + for prop in ("mega_bytes_per_second", "cpu_time_ms"): + ps, ps_ = [], [] + pd = bm_panel.first_run.property_plot_data(prop) + bar_label = f"{pd.human_name_short}{pd.what_is_better}" + outfilename = f"{outputfile_prefix}-{prop}" + for bm in bm_panel.runs: + bar_names = [m.shorttitle for m in bm.meta] + bar_values = list(getattr(bm, prop)) + data_type = first(bm.meta).data_type + # to save each bokeh plot separately and also + # a grid plot with all of them, we have to plot + # twice because bokeh does not allow saving twice + # the same plot from multiple pictures. + plotit = lambda: plot_benchmark_run_as_bars(bm, title=f"{panel_title_human}: {data_type}\n{bar_label}", + bar_names=bar_names, bar_values=bar_values, bar_label=bar_label) + # make one plot to save: + p, p_ = plotit() + _bokeh_save_html(f"{outfilename}-{data_type.short}.html", p) + _plt_save_png_and_clear(f"{outfilename}-{data_type.short}.png") + # and another to gather: + p, p_ = plotit() + ps.append(p) + ps_.append(p_) + layout = bkl.gridplot(ps, ncols=2) + _bokeh_save_html(f"{outfilename}.html", layout) + # now show + #bkp.show(layout) + + +def plot_charconv_threads_(bm_panel: BenchmarkPanel, panel_title_human: str, outputfile_prefix: str): + assert os.path.isabs(outputfile_prefix), outputfile_prefix + orig = lambda yprop: lambda bm: list(bm.extract_plot_series(yprop)) + divnt = lambda yprop: lambda bm: [v / n for v, n in bm.extract_plot_series_with_threads(yprop)] + mulnt = lambda yprop: lambda bm: [v * n for v, n in bm.extract_plot_series_with_threads(yprop)] + xprop = "threads" + xpd = bm_panel.first_run.property_plot_data(xprop) + xlabel = f"{xpd.human_name_short}" + for yprop, ylog, yget in ( + #("mega_items_per_second", False, orig), + ("mega_bytes_per_second", False, orig), + #("iterations", False, divnt), + #("real_time_ms", True, mulnt), + ("cpu_time_ms", True, orig),): + ypd = bm_panel.first_run.property_plot_data(yprop) + ylabel = f"{ypd.human_name_short}{ypd.what_is_better}" + p = plot_benchmark_panel_as_lines( + bm_panel, f"{panel_title_human}\n{ylabel}", + xget=orig("threads"), + yget=yget(yprop), + nameget=lambda bm: first(bm.meta).function, + ylog=ylog, + xlabel=xlabel, + ylabel=ylabel + ) + name = f"{outputfile_prefix}-lines-{yprop}" + # save png using matplotlib + _plt_save_png_and_clear(f"{name}.png") + # save html using bokeh + _bokeh_save_html(f"{name}.html", p) + #bkp.show(p) + return p + + +def plot_charconv_threads(json_files, case: str = ""): + case = f" [{case}]" if case else "" + dir_ = os.path.dirname(first(json_files)) + panel = BenchmarkPanel(json_files, CharconvThreadsMeta) + plot_charconv_threads_(panel, + f"itoa benchmark: convert 2M 32b integers to string{case}", + f"{dir_}/c4core-bm-charconv_threads") + + +def plot_charconv_xtoa(json_files, case: str = ""): + case = f" [{case}]" if case else "" + dir_ = os.path.dirname(first(json_files)) + panel = BenchmarkPanel(json_files, CharconvMeta) + plot_charconv_bars(panel, + f"xtoa benchmark: convert 2M numbers to strings{case}", + f"{dir_}/c4core-bm-charconv-xtoa") + + +def plot_charconv_atox(json_files, case: str = ""): + case = f" [{case}]" if case else "" + dir_ = os.path.dirname(first(json_files)) + panel = BenchmarkPanel(json_files, CharconvMeta) + plot_charconv_bars(panel, + f"atox benchmark: convert 2M strings to numbers{case}", + f"{dir_}/c4core-bm-charconv-atox") + + +def threads_data(dir_: str): + assert os.path.exists(dir_), dir_ + return [ + f"{dir_}/c4core-bm-charconv_threads-c4_write_dec.json", + f"{dir_}/c4core-bm-charconv_threads-c4_itoa.json", + f"{dir_}/c4core-bm-charconv_threads-c4_xtoa.json", + f"{dir_}/c4core-bm-charconv_threads-c4_to_chars.json", + f"{dir_}/c4core-bm-charconv_threads-fmtlib_format_to.json", + f"{dir_}/c4core-bm-charconv_threads-std_to_chars.json", + f"{dir_}/c4core-bm-charconv_threads-snprintf.json", + f"{dir_}/c4core-bm-charconv_threads-stb_snprintf.json", + f"{dir_}/c4core-bm-charconv_threads-sstream.json", + f"{dir_}/c4core-bm-charconv_threads-sstream_naive_reuse.json", + f"{dir_}/c4core-bm-charconv_threads-sstream_naive.json", + ] + + +def xtoa_data(dir_: str): + assert os.path.exists(dir_), dir_ + return [ + f"{dir_}/c4core-bm-charconv-xtoa-int8.json", + f"{dir_}/c4core-bm-charconv-xtoa-uint8.json", + f"{dir_}/c4core-bm-charconv-xtoa-int16.json", + f"{dir_}/c4core-bm-charconv-xtoa-uint16.json", + f"{dir_}/c4core-bm-charconv-xtoa-int32.json", + f"{dir_}/c4core-bm-charconv-xtoa-uint32.json", + f"{dir_}/c4core-bm-charconv-xtoa-int64.json", + f"{dir_}/c4core-bm-charconv-xtoa-uint64.json", + ] + + +def atox_data(dir_: str): + assert os.path.exists(dir_), dir_ + return [ + f"{dir_}/c4core-bm-charconv-atox-int8.json", + f"{dir_}/c4core-bm-charconv-atox-uint8.json", + f"{dir_}/c4core-bm-charconv-atox-int16.json", + f"{dir_}/c4core-bm-charconv-atox-uint16.json", + f"{dir_}/c4core-bm-charconv-atox-int32.json", + f"{dir_}/c4core-bm-charconv-atox-uint32.json", + f"{dir_}/c4core-bm-charconv-atox-int64.json", + f"{dir_}/c4core-bm-charconv-atox-uint64.json", + ] + + +def examples_dir(): + this_dir = os.path.dirname(os.path.abspath(__file__)) + exdir = f"{this_dir}/examples" + assert os.path.exists(exdir), exdir + return exdir + + +if __name__ == '__main__': + xdir = examples_dir() + # + plot_charconv_threads(threads_data(f"{xdir}/lines/gcc11.2"), "gcc11.2") + plot_charconv_threads(threads_data(f"{xdir}/lines/vs2022"), "vs2022") + # + plot_charconv_xtoa(xtoa_data(f"{xdir}/bars/xtoa/gcc11.2"), "gcc11.2") + plot_charconv_xtoa(xtoa_data(f"{xdir}/bars/xtoa/vs2022"), "vs2022") + # + plot_charconv_atox(atox_data(f"{xdir}/bars/atox/gcc11.2"), "gcc11.2") + plot_charconv_atox(atox_data(f"{xdir}/bars/atox/vs2022"), "vs2022") + # + exit() + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + + +def plot_benchmarks_as_lines(title, *bm, transform=None, + line_title_transform=None, + logx=True, logy=True): + import bokeh + from bokeh.plotting import figure, output_file, show + from bokeh.palettes import Dark2_5 as palette + from bokeh.layouts import row, column + from bokeh.models import (Legend, LegendItem, CheckboxGroup, CustomJS, Div, + RadioGroup, Toggle, + ColumnDataSource, DataTable, TableColumn) + from bokeh.models.markers import marker_types + # + ids = entry_ids(*bm, transform=transform) + colors = itertools.cycle(palette) + markers = itertools.cycle(marker_types) + p = figure(title=title, + x_axis_type="log" if logx else "linear", + y_axis_type="log" if logy else "linear", + #background_fill_color="#fafafa", + plot_width=1000, + x_axis_label="Number of pixels", + y_axis_label="Throughput (MB/s)", + ) + p.toolbar.autohide = True + #p.toolbar.active_inspect = [hover_tool, crosshair_tool] + p.toolbar.active_drag = "auto" + p.toolbar.active_scroll = "auto" + # + def dft(v): return v if v else (lambda n: n) + tr = dft(transform) + lttr = dft(line_title_transform) + # + for results in bm: + x = [ids[name] for name in results.names] + y = [bps/1e6 for bps in results.bytes_per_second] + c = next(colors) + marker = next(markers) + next(markers) # advance two + line_name = lttr(results.first) + #legends.append(LegendItem(name=c, label=line_name)) + p.scatter(x, y, marker=marker, size=8, color=c, legend_label=line_name) + p.line(x, y, + color=c, alpha=0.9, + #muted_color=c, muted_alpha=0.05, + legend_label=line_name) + p.legend.click_policy = "hide" + p.legend.label_text_font_size = "10px" + # + def input_title(title): + return Div(text=f"<h3>{title}</h3>") + inputs = [] + first = bm[0].first.meta + for k, g in first.checkbox_groups().items(): + cb = CheckboxGroup(labels=[str(v) for v in g], + active=[i for i in range(len(g))], + inline=True) + inputs.append(input_title(k)) + inputs.append(cb) + # + # https://github.com/bokeh/bokeh/blob/branch-2.3/examples/app/export_csv/main.py + x_axis_values = [f"{m.num_pixels}px" for m in bm[0].meta] + table_sources = [] + for i, px in enumerate(x_axis_values): + c = ColumnDataSource(data={ + 'name': [nth(results.filtered_names, i) for results in bm], + 'bytes_per_second': [nth(results.bytes_per_second, i) for results in bm], + 'items_per_second': [nth(results.items_per_second, i) for results in bm], + 'cpu_time': [nth(results.real_time, i) for results in bm], + 'real_time': [nth(results.real_time, i) for results in bm], + 'iterations': [nth(results.iterations, i) for results in bm], + 'threads': [nth(results.threads, i) for results in bm], + }) + table_sources.append(c) + selected_x_index = 8 # FIXME (currently 2000 pixels) + table_source = copy.deepcopy(table_sources[selected_x_index]) + relvalues = Toggle(label="Table: Relative values") + px_title = input_title("Table: number of pixels") + px_radiogroup = RadioGroup(labels=x_axis_values, active=selected_x_index) + table_inputs = [relvalues, px_title, px_radiogroup] + # + table_cols = [ + TableColumn(field='name', title='Name'), + TableColumn(field='bytes_per_second', title='Bytes/second'), + TableColumn(field='items_per_second', title='Items/second'), + TableColumn(field='cpu_time', title='CPU time'), + TableColumn(field='real_time', title='Real time'), + TableColumn(field='iterations', title='Iterations'), + TableColumn(field='threads', title='Threads'), + ] + data_table = DataTable(source=table_source, columns=table_cols, width=1200) + callback = CustomJS(args=dict( + radiogroup=px_radiogroup, + source=table_source, + table=table_sources + ), code=""" + console.log(`active=${radiogroup.active}`); + /*source.data=table[radiogroup.active];*/ + var nrows = source.data['name'].length; + var ts = table[radiogroup.active].data; + var names = ["name", "bytes_per_second", "items_per_second", "cpu_time", "real_time", "iterations", "threads"]; + var ncols = names.length; + console.log(`names=${names} nrows=${nrows} ncols=${ncols}`); + for(var i = 0; i < nrows; i++) { + for(var j = 0; j < ncols; ++j) { + var name = names[j]; + /*console.log(`i=${i} j=${j} name=${name}`);*/ + source.data[name][i] = ts[name][i]; + } + } + source.change.emit(); + """) + px_radiogroup.js_on_change('active', callback) + # lambda attr, old, new: log(f"attr={attr} old={old} new={new} active={px_radiogroup.active}")) + # + layout = column( + row(column(*inputs), p), + row(column(*table_inputs), data_table)) + show(layout) + + +def entry_ids(*bm, transform=None): + ids = {} + curr = 0 + for results in bm: + log(os.path.basename(results.filename), "------------------------------") + for entry in results.entries: + log(entry.name) + if transform is not None: + ids[entry.name] = transform(entry) + else: + if ids.get(entry.name) is None: + ids[entry.name] = curr + curr += 1 + return ids + + +class MatrixOrder(_enum): + row_major = "row_major" + col_major = "col_major" + @property + def short(self): + return "rm" if self is MatrixOrder.row_major else "cm" + @classmethod + def make(cls, s): + try: + return {"rm": cls.row_major, "cm": cls.col_major}[s] + except: + cls.err_unknown(s) + + +class MatrixLayout(_enum): + compact = "compact" + strided = "strided" + @classmethod + def make(cls, s): + try: + return cls[s] + except: + cls.err_unknown(s) + + +class DimensionBinding(_enum): + compile_time = "compile_time" + run_time = "run_time" + @property + def short(self): + return "ct" if self is DimensionBinding.compile_time else "rt" + @classmethod + def make(cls, s): + try: + return {"ct": cls.compile_time, "rt": cls.run_time}[s] + except: + cls.err_unknown(s) + + +class MultType(_enum): + naive = "naive" + avx2 = "avx2" + avx2_unroll2 = "avx2_unroll2" + avx2_unroll4 = "avx2_unroll4" + avx2_unroll8 = "avx2_unroll8" + @classmethod + def make(cls, s): + try: + s = s.replace("dotprod_", "").replace("_naive", "") + return cls[s] + except: + cls.err_unknown(s) + + +class MatrixMult(typing.NamedTuple): + title: str + num_pixels: int + num_channels: int + num_features: int + mult_type: MultType + layout: MatrixLayout + dim_binding: DimensionBinding + ret_order: MatrixOrder + lhs_order: MatrixOrder + rhs_order: MatrixOrder + + @classmethod + def make(cls, bm_title: str): + # eg: + # mult_naive_strided_ct_rm_cmcm<250, 8, 16> + # mult_naive_compact_rt_rm_rmrm/4000/8/16 + rxline = r'mult_(.*)[</](\d+)(?:/|, )(\d+)(?:/|, )(\d+).*' + rxcase = r"(.*)_(compact|strided)_(ct|rt)_(rm|cm)_(rm|cm)(rm|cm)" + case = re.sub(rxline, r'\1', bm_title) + return cls( + title=case, + num_pixels=int(re.sub(rxline, r'\2', bm_title)), + num_channels=int(re.sub(rxline, r'\3', bm_title)), + num_features=int(re.sub(rxline, r'\4', bm_title)), + mult_type=MultType.make(re.sub(rxcase, r'\1', case)), + layout=MatrixLayout.make(re.sub(rxcase, r'\2', case)), + dim_binding=DimensionBinding.make(re.sub(rxcase, r'\3', case)), + ret_order=MatrixOrder.make(re.sub(rxcase, r'\4', case)), + lhs_order=MatrixOrder.make(re.sub(rxcase, r'\5', case)), + rhs_order=MatrixOrder.make(re.sub(rxcase, r'\6', case)) + ) + + def comparison_axes(self): + return ('num_pixels', 'num_channels', 'num_features') + + def checkbox_groups(self): + return { + 'multiplication method': [t for t in MultType], + 'layout': [t for t in MatrixLayout], + 'dimension': [d for d in DimensionBinding], + 'return matrix ordering': [o for o in MatrixOrder], + 'lhs matrix ordering': [o for o in MatrixOrder], + 'rhs matrix ordering': [o for o in MatrixOrder], + } + + @property + def matrix_size(self): + return self.num_pixels * self.num_channels + + @property + def classifier_size(self): + return self.num_channels * self.num_features + + @property + def shortname(self): + m = self + return f"{m.mult_type}/{m.layout}/{m.dim_binding.short}_{m.ret_order.short}_{m.lhs_order.short}{m.rhs_order.short}" + + @property + def shortparams(self): + m = self + return f"{m.num_pixels:04d}px{m.num_channels:02d}ch{m.num_features:02d}ft" + + @property + def shorttitle(self): + return f"{self.shortname}/{self.shortparams}" + + +def _test(): + def expect(v_, attr, val): + var = getattr(v_, attr) + if var != val: + raise Exception(f"{attr}: expected={val} actual={var}") + # + v = MatrixMult.make("mult_naive_strided_ct_rm_cmcm<250, 8, 16>") + expect(v, 'title', 'naive_strided_ct_rm_cmcm') + expect(v, 'num_pixels', 250) + expect(v, 'num_channels', 8) + expect(v, 'num_features', 16) + expect(v, 'mult_type', MultType.naive) + expect(v, 'layout', MatrixLayout.strided) + expect(v, 'dim_binding', DimensionBinding.compile_time) + expect(v, 'ret_order', MatrixOrder.row_major) + expect(v, 'lhs_order', MatrixOrder.col_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + v = MatrixMult.make("mult_dotprod_avx2_compact_rt_cm_rmcm/4000/16/8") + expect(v, 'title', 'dotprod_avx2_compact_rt_cm_rmcm') + expect(v, 'num_pixels', 4000) + expect(v, 'num_channels', 16) + expect(v, 'num_features', 8) + expect(v, 'mult_type', MultType.avx2) + expect(v, 'layout', MatrixLayout.compact) + expect(v, 'dim_binding', DimensionBinding.run_time) + expect(v, 'ret_order', MatrixOrder.col_major) + expect(v, 'lhs_order', MatrixOrder.row_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + +_test() + + + +def formatMBps(value): + return value / 1e6 + + + +if __name__ == '__main__': + bms = sorted(sys.argv[2:]) + log(bms) + bms = BenchmarkPanel(bms, bm_meta_cls=MatrixMult.make) + fm = bms.runs[0].first.meta + title = f"Classifier multiplication, {fm.num_channels} channels, {fm.num_features} features: throughput (MB/s)" + bms.plot_all_lines(title) + exit() + main() diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_run.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_run.py new file mode 100644 index 000000000..812b64abe --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_run.py @@ -0,0 +1,248 @@ +import copy +import os.path + +# https://stackoverflow.com/questions/11351032/named-tuple-and-default-values-for-optional-keyword-arguments +from dataclasses import dataclass + +from munch import Munch + +from bm_util import load_json, first, _enum + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class QuantityType(_enum): + neutral = "" + more_is_better = "more is better" + less_is_better = "less is better" + + @property + def comment(self): + return f" ({self.value})" if self.name else "" + + +_more = QuantityType.more_is_better +_less = QuantityType.less_is_better + + +@dataclass +class BenchmarkPropertyPlotData: + human_name: str = "" + human_name_short: str = "" + qty_type: QuantityType = QuantityType.neutral + + +class BenchmarkRun(Munch): + """results of an individual run""" + + def __init__(self, json_file: str, meta_class): + """ + meta_class is a class to extract property values from the benchmark run + """ + self._filename = json_file + props = load_json(json_file) + assert hasattr(props, "context") + assert hasattr(props, "benchmarks") + super().__init__(**props) + setattr(self, 'property_names', list(__class__._properties.keys())) + for bm in self.benchmarks: + if meta_class is not None: + setattr(bm, 'meta', meta_class.make(bm.name)) + else: + setattr(bm, 'meta', None) + + _properties = { + 'filename': None, + 'basename': None, + 'dirname': None, + 'meta': None, + 'shorttitle': None, + 'name': None, + 'run_name': None, + 'run_type': None, + 'repetitions': None, + 'repetition_index': None, + 'threads': BenchmarkPropertyPlotData('number of threads', 'threads'), + 'iterations': BenchmarkPropertyPlotData('number of iterations', 'iterations', _more), + 'real_time': BenchmarkPropertyPlotData('real time', 'real time', _less), + 'cpu_time': BenchmarkPropertyPlotData('CPU time', 'cpu time', _less), + 'real_time_ms': BenchmarkPropertyPlotData('real time', 'real time', _less), + 'cpu_time_ms': BenchmarkPropertyPlotData('CPU time', 'cpu time', _less), + 'time_unit': None, + 'bytes_per_second': BenchmarkPropertyPlotData('Bytes/s', 'B/s', _more), + 'items_per_second': BenchmarkPropertyPlotData('items/s', 'items/s', _more), + 'mega_bytes_per_second': BenchmarkPropertyPlotData('MBytes/s', 'MB/s', _more), + 'mega_items_per_second': BenchmarkPropertyPlotData('Mega items/s', 'Mega items/s', _more), + 'counters': None, + } + + def property_plot_data(self, property_name: str): + pd = copy.deepcopy(__class__._properties.get(property_name, BenchmarkPropertyPlotData())) + if property_name.endswith('_time'): + time_unit = first(self.entries).time_unit + pd.human_name += f' ({time_unit})' + pd.human_name_short += f' ({time_unit})' + elif property_name.endswith('_time_ms'): + pd.human_name += ' (ms)' + pd.human_name_short += ' (ms)' + return pd + + def extract_plot_series(self, property_name_or_getter, + relative_to_entry = None, + percent_of_entry = None, + ): + if isinstance(property_name_or_getter, str): + series = getattr(self, property_name_or_getter) + else: + series = property_name_or_getter(self) + series = list(series) + def getrefval(ref): + assert ref in self.entries, ref.name + pos = self.pos(ref) + assert pos in range(len(series)), (pos, len(series)) + return series[pos] + if relative_to_entry: + refval = getrefval(relative_to_entry) + for v in series: + yield v / refval + elif percent_of_entry: + refval = getrefval(percent_of_entry) + for v in series: + yield 100.0 * ((v - refval) / refval) + else: + for v in series: + yield v + + def extract_plot_series_with_threads(self, property_name_or_getter, + relative_to: str = None, + percent_of: str = None, + ): + series = self.extract_plot_series(property_name_or_getter, relative_to=relative_to, percent_of=percent_of) + for y, n in zip(series, self.threads): + yield y, n + + def pos(self, entry): + for i, e in enumerate(self.entries): + if e == entry: + return i + raise Exception("entry not found") + + @property + def filename(self): + return self._filename + + @property + def basename(self): + return os.path.basename(self._filename) + + @property + def dirname(self): + return os.path.dirname(self._filename) + + @property + def entries(self): + for entry in self.benchmarks: + yield entry + + @property + def meta(self): + for entry in self.benchmarks: + yield entry.meta + + @property + def names(self): + for entry in self.benchmarks: + yield entry.name + + @property + def run_names(self): + for entry in self.benchmarks: + yield entry.run_name + + @property + def run_types(self): + for entry in self.benchmarks: + yield entry.run_type + + @property + def repetitions(self): + for entry in self.benchmarks: + yield entry.repetitions + + @property + def repetition_indices(self): + for entry in self.benchmarks: + yield entry.repetition_index + + @property + def threads(self): + for entry in self.benchmarks: + yield entry.threads + + @property + def iterations(self): + for entry in self.benchmarks: + yield entry.iterations + + @property + def real_time(self): + for entry in self.benchmarks: + yield entry.real_time + + @property + def cpu_time(self): + for entry in self.benchmarks: + yield entry.cpu_time + + @property + def real_time_ms(self): + for entry in self.benchmarks: + assert entry.time_unit == "ns" + yield entry.real_time / 1e6 + + @property + def cpu_time_ms(self): + for entry in self.benchmarks: + assert entry.time_unit == "ns" + yield entry.cpu_time / 1e6 + + @property + def time_unit(self): + for entry in self.benchmarks: + yield entry.time_unit + + @property + def bytes_per_second(self): + for entry in self.benchmarks: + yield entry.bytes_per_second + + @property + def items_per_second(self): + for entry in self.benchmarks: + yield entry.items_per_second + + @property + def mega_bytes_per_second(self): + for entry in self.benchmarks: + yield entry.bytes_per_second / 1e6 + + @property + def mega_items_per_second(self): + for entry in self.benchmarks: + yield entry.items_per_second / 1e6 + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkPanel: + + def __init__(self, runs, bm_meta_cls=None): + self.runs = [BenchmarkRun(a, bm_meta_cls) for a in runs] + + @property + def first_run(self): + return first(self.runs) diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_serve.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_serve.py new file mode 100644 index 000000000..6fc683b84 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_serve.py @@ -0,0 +1,502 @@ +import os +import sys +import argparse +import copy +import requests +import flask +import json +import re +import yaml +import shutil +import mmh3 +import itertools +import typing +import enum + +# https://stackoverflow.com/questions/11351032/named-tuple-and-default-values-for-optional-keyword-arguments +from dataclasses import dataclass + +from munch import Munch, munchify +from flask import render_template, redirect, url_for, send_from_directory +from markupsafe import escape + +from bm_util import * + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkCollection: + + @staticmethod + def create_new(args): + dir = args.target + filename = os.path.join(dir, "bm.yml") + manifest = os.path.join(dir, "manifest.yml") + if not os.path.exists(dir): + os.makedirs(dir) + shutil.copyfile(args.filename, filename) + dump_yml(load_yml("""{runs: {}, bm: {}}"""), manifest) + return __class__(dir) + + def __init__(self, dir): + if not os.path.exists(dir): + raise Exception(f"not found: {dir}") + self.dir = os.path.abspath(dir) + self.runs_dir = os.path.join(self.dir, "runs") + self.manifest = os.path.join(self.dir, "manifest.yml") + self.filename = os.path.join(self.dir, "bm.yml") + self.specs = munchify(load_yml_file(self.filename)) + self.manif = munchify(load_yml_file(self.manifest)) + + def add(self, results_dir): + results_dir = os.path.abspath(results_dir) + dst_dir, meta = self._read_run(results_dir) + self._add_run(results_dir, dst_dir, meta) + dump_yml(self.manif, self.manifest) + + def _read_run(self, results_dir): + log("adding run...") + id = f"{len(self.manif.runs.keys()):05d}" + log(f"adding run: id={id}") + meta = ResultMeta.load(results_dir) + dst_dir = os.path.join(self.runs_dir, meta.name) + return dst_dir, meta + + def _add_run(self, results_dir, dst_dir, meta): + cats = self._add_meta_categories(meta) + for filename in ("meta.yml", + "CMakeCCompiler.cmake", + "CMakeCXXCompiler.cmake", + "CMakeSystem.cmake", + "compile_commands.json"): + filename = os.path.join(results_dir, filename) + if os.path.exists(filename): + copy_file_to_dir(filename, dst_dir) + else: + if not filename.endswith("compile_commands.json"): + raise Exception(f"wtf???? {filename}") + for name, specs in self.specs.bm.items(): + if not hasattr(specs, 'variants'): + filename = chk(f"{results_dir}/{name}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(name, specs, meta) + else: + for t in specs.variants: + tname = f"{name}-{t}" + filename = chk(f"{results_dir}/{tname}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(tname, specs, meta) + + def _add_bm_run(self, name, specs, meta): + if name not in self.manif.bm.keys(): + self.manif.bm[name] = Munch(specs=specs, entries=[]) + entry = self.manif.bm[name] + entry.specs = specs + if meta.name not in entry.entries: + entry.entries.append(meta.name) + + def _add_meta_categories(self, meta): + run = Munch() + for catname in ('commit', 'cpu', 'system', 'build'): + meta_item = getattr(meta, catname) + self._add_item_to_category(meta.name, catname, meta_item) + run[catname] = meta_item.storage_id + # build specs are too verbose; remove them + self.manif.build[meta.build.storage_id].specs = Munch() + self.manif.runs[meta.name] = run + + def _add_item_to_category(self, run, category_name, item): + if not hasattr(self.manif, category_name): + setattr(self.manif, category_name, Munch()) + category = getattr(self.manif, category_name) + if item.storage_id not in category.keys(): + category[item.storage_id] = Munch(specs=item, entries=[]) + entry = category[item.storage_id] + entry.specs = item + if run not in entry.entries: + entry.entries.append(run) + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class ResultMeta(Munch): + + def __init__(self, results_dir, cmakecache, build_type): + super().__init__(self) + self.date = __class__.get_date() + self.commit = __class__.get_commit(results_dir) + self.cpu = __class__.get_cpu_info() + self.system = __class__.get_sys_info() + self.build = __class__.get_build_info(cmakecache, build_type) + self.name = self._get_name() + + @staticmethod + def load(results_dir): + results_dir = os.path.join(os.path.abspath(results_dir), "meta.yml") + data = load_yml_file(results_dir) + return munchify(data) + + def save(self, results_dir): + out = os.path.join(results_dir, "meta.yml") + log("saving meta:", out) + dump_yml(self, out) + self.build.save(results_dir) + + @staticmethod + def get_date(): + import datetime + now = datetime.datetime.now() + return now.strftime("%Y%m%d-%H%M%S") + + def _get_name(self): + commit = self.commit.storage_name + cpu = self.cpu.storage_name + sys = self.system.storage_name + build = self.build.storage_name + name = f"{commit}/{cpu}-{sys}-{build}" + return name + + @staticmethod + def get_commit(results_dir): + import git + repo = git.Repo(results_dir, search_parent_directories=True) + commit = repo.head.commit + commit = {p: str(getattr(commit, p)) + for p in ('message', 'summary', 'name_rev', + 'author', + 'authored_datetime', + 'committer', + 'committed_datetime',)} + commit = Munch(commit) + commit.message = commit.message.strip() + commit.sha1 = commit.name_rev[:7] + spl = commit.authored_datetime.split(" ") + date = re.sub(r'-', '', spl[0]) + time = re.sub(r'(\d+):(\d+):(\d+).*', r'\1\2\3', spl[1]) + commit.storage_id = commit.sha1 + commit.storage_name = f"git{date}_{time}-{commit.sha1}" + return commit + + @staticmethod + def get_cpu_info(): + import cpuinfo + nfo = cpuinfo.get_cpu_info() + nfo = Munch(nfo) + for a in ('cpu_version', 'cpu_version_string', 'python_version'): + if hasattr(nfo, a): + delattr(nfo, a) + for a in ('arch_string_raw', 'brand_raw', 'hardware_raw', 'vendor_id_raw'): + if not hasattr(nfo, a): + setattr(nfo, a, '') + nfo.storage_id = myhash( + nfo.arch_string_raw, nfo.brand_raw, nfo.hardware_raw, nfo.vendor_id_raw, + nfo.arch, nfo.bits, nfo.count, nfo.family, nfo.model, nfo.stepping, + ",".join(nfo.flags), nfo.hz_advertised_friendly, + nfo.l2_cache_associativity, + nfo.l2_cache_line_size, + nfo.l2_cache_size, + nfo.l3_cache_size, + *optionals('l1_data_cache_size', 'l1_instruction_cache_size') + ) + nfo.storage_name = f"{nfo.arch.lower()}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_sys_info(): + import platform + uname = platform.uname() + nfo = Munch( + sys_platform=sys.platform, + sys=platform.system(), + uname=Munch( + machine=uname.machine, + node=uname.node, + release=uname.release, + system=uname.system, + version=uname.version, + ) + ) + nfo.storage_id = myhash( + nfo.sys_platform, + nfo.uname.machine, + ) + nfo.storage_name = f"{nfo.sys_platform}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_build_info(cmakecache_txt, buildtype): + nfo = CMakeCache(cmakecache_txt) + def _btflags(name): + return (getattr(nfo, name), getattr(nfo, f"{name}_{buildtype.upper()}")) + nfo.storage_id = myhash( + buildtype, + nfo.CMAKE_CXX_COMPILER_ID, + nfo.CMAKE_CXX_COMPILER_VERSION, + nfo.CMAKE_CXX_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_CXX_COMPILER_ABI, + nfo.CMAKE_CXX_SIZEOF_DATA_PTR, + nfo.CMAKE_C_COMPILER_ID, + nfo.CMAKE_C_COMPILER_VERSION, + nfo.CMAKE_C_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_C_COMPILER_ABI, + nfo.CMAKE_C_SIZEOF_DATA_PTR, + *_btflags("CMAKE_CXX_FLAGS"), + *_btflags("CMAKE_C_FLAGS"), + *_btflags("CMAKE_STATIC_LINKER_FLAGS"), + *_btflags("CMAKE_SHARED_LINKER_FLAGS"), + ) + # + ccname = nfo.CMAKE_CXX_COMPILER_ID.lower() + if ccname == "gnu": + ccname = "gcc" + ccname += nfo.CMAKE_CXX_COMPILER_VERSION.lower() + # + if nfo.CMAKE_C_SIZEOF_DATA_PTR == "4": + bits = "32bit" + elif nfo.CMAKE_C_SIZEOF_DATA_PTR == "8": + bits = "64bit" + else: + raise Exception("unknown architecture") + # + nfo.storage_name = f"{bits}_{buildtype}_{ccname}_{nfo.storage_id}" + return nfo + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class CMakeCache(Munch): + + def __init__(self, cmakecache_txt): + import glob + for line in iter_cmake_lines(cmakecache_txt): + spl = line.split("=") + if len(spl) < 2: + continue + k, ty = spl[0].split(":") + v = "=".join(spl[1:]).strip() + setattr(self, k, v) + bdir = os.path.dirname(os.path.abspath(cmakecache_txt)) + self._c_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCCompiler.cmake"))[-1] # get the last + self._cxx_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCXXCompiler.cmake"))[-1] # get the last + self._system_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeSystem.cmake"))[-1] # get the last + self._load_cmake_file(self._c_compiler_file) + self._load_cmake_file(self._cxx_compiler_file) + ccomfile = f"{bdir}/compile_commands.json" + self._compile_commands_file = ccomfile if os.path.exists(ccomfile) else None + + def _load_cmake_file(self, filename): + for line in iter_cmake_lines(filename): + if not line.startswith("set("): + continue + k = re.sub(r"set\((.*)\ +(.*)\)", r"\1", line) + v = re.sub(r"set\((.*)\ +(.*)\)", r"\2", line) + v = v.strip('"').strip("'").strip() + setattr(self, k, v) + + def save(self, results_dir): + copy_file_to_dir(self._c_compiler_file, results_dir) + copy_file_to_dir(self._cxx_compiler_file, results_dir) + copy_file_to_dir(self._system_file, results_dir) + if self._compile_commands_file is not None: + copy_file_to_dir(self._compile_commands_file, results_dir) + + +def iter_cmake_lines(filename): + with open(filename) as f: + for line in f.readlines(): + line = line.strip() + if line.startswith("#") or line.startswith("//") or len(line) == 0: + continue + yield line + + +# -------------------------------------------------------- + + +def get_manifest(args): + bmdir = os.path.abspath(args.bmdir) + if not args.manifest: + manifest_yml = os.path.join(bmdir, "manifest.yml") + else: + if not os.path.isabs(args.manifest): + manifest_yml = os.path.join(os.getcwd(), args.manifest) + manifest_json = os.path.join(os.path.dirname(manifest.yml), "manifest.json") + manifest = load_yml_file(manifest_yml) + dump_json(manifest, manifest_json) + return manifest + + +def add_results(args): + log("adding results:", args.results) + col = BenchmarkCollection(args.target) + col.add(args.results) + + +def add_meta(args): + log("adding bm run metadata to results dir:", args.results) + meta = ResultMeta(results_dir=args.results, + cmakecache=args.cmakecache, + build_type=args.build_type) + meta.save(args.results) + log("adding bm run metadata to results dir: success!") + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +app = flask.Flask(__name__, template_folder='template') + +def _setup_app(args): + def _s(prop, val): + assert not hasattr(app, prop), prop + setattr(app, prop, val) + _s('args', args) + _s('manifest', get_manifest(args)) + if args.debug: + app.config["DEBUG"] = True + + +def freeze(args): + "https://pythonhosted.org/Frozen-Flask/" + from flask_frozen import Freezer + _setup_app(args) + freezer = Freezer(app) + freezer.freeze(debug=args.debug) + + +def serve(args): + _setup_app(args) + app.run(host=args.host, port=args.port, debug=args.debug) + + [email protected]("/") +def home(): + log("requested home") + return render_template("index.html") + + [email protected]("/<path>") +def other_(path): + path = escape(path) + d = app.args.bmdir + log("requested other path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path) + + [email protected]("/static/<path>") +def static_(path): + path = escape(path) + d = os.path.join(app.args.bmdir, "static") + log("requested static path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path, cache_timeout=1) # timeout in seconds + + [email protected]("/bm/<commit>/<run>/<resultjson>") +def bm_(commit, run, resultjson): + commit = escape(commit) + run = escape(run) + resultjson = escape(resultjson) + d = os.path.join(app.args.bmdir, "runs", commit, run) + log("requested result:", os.path.join(d, resultjson)) + return send_from_directory(d, resultjson, cache_timeout=1) # timeout in seconds + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def download_deps(): + deps = [ + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/ui/1.12.1/jquery-ui.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css", + "https://www.chartjs.org/dist/2.9.1/Chart.min.js", + #("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.css", "highlight.github.css"), + ("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.min.css", "highlight.github.min.css"), + #"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.js", + "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.min.js", + ] + for src in deps: + if type(src) == str: + base = os.path.basename(src) + else: + src, base = src + dst = f"{os.getcwd()}/static/{base}" + download_url(src, dst) + + +def download_url(url, dst): + log("download url:", url, "--->", dst) + req = requests.get(url, stream=True) + if req.status_code == 200: + sz = 0 + with open(dst, 'wb') as f: + for chunk in req: + f.write(chunk) + sz += len(chunk) + log(f"........ finished: {sz}B") + else: + log(f" error:", req.status_code, url) + + + +def main(): + def _common_args(parser): + parser.add_argument("-m", "--manifest", type=str, default="", help="enable debug mode") + parser.add_argument("--debug", action="store_true", help="enable debug mode") + # + parser = argparse.ArgumentParser(description="Browse benchmark results", prog="bm") + _common_args(parser) + subparsers = parser.add_subparsers() + # + sp = subparsers.add_parser("create", help="create benchmark collection") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("filename", type=str, help="the YAML file with the benchmark specs") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("meta", help="get the required meta-information: cpu info, commit data") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("cmakecache", type=str, help="the path to the CMakeCache.txt file used to build the benchmark binaries") + sp.add_argument("build_type", type=str, help="the build type, eg Release Debug MinSizeRel RelWithDebInfo") + # + sp = subparsers.add_parser("add", help="add benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("serve", help="serve benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + sp.add_argument("-H", "--host", type=str, default="localhost", help="host. default=%(default)s") + sp.add_argument("-p", "--port", type=int, default=8000, help="port. default=%(default)s") + # + sp = subparsers.add_parser("export", help="export static html") + sp.set_defaults(func=freeze) + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + _common_args(sp) + # + sp = subparsers.add_parser("deps", help="install server dependencies") + sp.set_defaults(func=lambda _: download_deps()) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + _common_args(sp) + # + args = parser.parse_args(sys.argv[1:] if len(sys.argv) > 1 else ["serve"]) + if args.debug: + log(args) + args.func(args) diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_util.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_util.py new file mode 100644 index 000000000..7c7c2891e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_util.py @@ -0,0 +1,147 @@ +import os +import json +import yaml +import shutil +import mmh3 +import itertools +import munch +import enum + + +# -------------------------------------------------------- + +class _enum(enum.Enum): + def __str__(self): + return str(self.name) + + @property + def short(self): + return self.name + + @classmethod + def make(cls, s): + try: + return cls[s] + except: + cls.err_unknown(s) + + @classmethod + def err_unknown(cls, s): + raise Exception(f"unknown {__class__.__name__}: {s}") + + +class FundamentalTypes(_enum): + float = "float" + double = "double" + int8_t = "int8_t" + uint8_t = "uint8_t" + int16_t = "int16_t" + uint16_t = "uint16_t" + int32_t = "int32_t" + uint32_t = "uint32_t" + int64_t = "int64_t" + uint64_t = "uint64_t" + @property + def short(self): + return self.name.replace("uint", "u").replace("int", "i").replace("_t", "") + + +# -------------------------------------------------------- + +def log(*args, **kwargs): + print(*args, **kwargs, flush=True) + + +def myhash_combine(curr, value): + return curr ^ (value + 0x9e3779b9 + (curr << 6) + (curr >> 2)) + + +def first(iterable): + """Returns the first item""" + if isinstance(iterable, list): + return iterable[0] + return next(iterable) + + +def chain(*iterables): + for it in iterables: + for elm in it: + yield elm + + +def nth(iterable, n, default=None): + """Returns the nth item or a default value""" + return next(itertools.islice(iterable, n, None), default) + + +def optionals(obj, *attrs): + ret = [] + for attr in attrs: + if not hasattr(obj, attr): + log("attr not present:", attr) + continue + ret.append(getattr(obj, attr)) + return ret + + +def myhash(*args): + h = 137597 + for a in args: + if isinstance(a, str): + if a == "": + continue + b = bytes(a, "utf8") + else: + b = bytes(a) + hb = mmh3.hash(b, signed=False) + h = myhash_combine(h, hb) + s = hex(h) + return s[2:min(10, len(s))] + + +def copy_file_to_dir(file, dir): + dir = os.path.abspath(dir) + src = os.path.abspath(file) + dst = f"{dir}/{os.path.basename(src)}" + if not os.path.exists(dir): + os.makedirs(dir) + if os.path.exists(dst): + os.remove(dst) + log("copy:", src, "-->", dst) + shutil.copy(src, dst) + return dst + + +def chk(f): + log("looking for file:", f) + assert os.path.exists(f), f + return f + + +def load_yml_file(filename): + if not os.path.exists(filename): + raise Exception(f"not found: {filename}") + with open(filename) as f: + return load_yml(f.read()) + + +def dump_yml(data, filename): + with open(filename, "w") as f: + yaml.safe_dump(data, f) + + +def load_yml(yml): + return munch.munchify(yaml.safe_load(yml)) + + +def dump_json(data, filename): + with open(filename, "w") as f: + f.write(json.dumps(data, indent=2, sort_keys=True)) + + +def load_json(filename): + with open(filename, "r") as f: + try: + return munch.munchify(json.load(f)) + except Exception as exc: + raise Exception(f"could not load file: {filename}: {exc}") diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_c4core.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_c4core.py new file mode 100644 index 000000000..3db4175cb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_c4core.py @@ -0,0 +1,1061 @@ +import os +import sys +import argparse +import copy +import requests +import flask +import json +import re +import yaml +import shutil +import mmh3 +from itertools import islice + +from munch import Munch, munchify +from flask import render_template, redirect, url_for, send_from_directory +from markupsafe import escape + + +def log(*args, **kwargs): + print(*args, **kwargs, flush=True) + + +def myhash_combine(curr, value): + return curr ^ (value + 0x9e3779b9 + (curr<<6) + (curr>>2)) + + +def nth(iterable, n, default=None): + "Returns the nth item or a default value" + return next(islice(iterable, n, None), default) + + +def optionals(obj, *attrs): + ret = [] + for attr in attrs: + if not hasattr(obj, attr): + log("attr not present:", attr) + continue + ret.append(getattr(obj, attr)) + return ret + + +def myhash(*args): + h = 137597 + for a in args: + if isinstance(a, str): + if a == "": + continue + b = bytes(a, "utf8") + else: + b = bytes(a) + hb = mmh3.hash(b, signed=False) + h = myhash_combine(h, hb) + s = hex(h) + return s[2:min(10, len(s))] + + +def copy_file_to_dir(file, dir): + dir = os.path.abspath(dir) + src = os.path.abspath(file) + dst = f"{dir}/{os.path.basename(src)}" + if not os.path.exists(dir): + os.makedirs(dir) + if os.path.exists(dst): + os.remove(dst) + log("copy:", src, "-->", dst) + shutil.copy(src, dst) + return dst + + +def chk(f): + log(f"looking for file:", f) + assert os.path.exists(f), f + return f + + +def load_yml_file(filename): + if not os.path.exists(filename): + raise Exception(f"not found: {filename}") + with open(filename) as f: + return load_yml(f.read()) + + +def dump_yml(data, filename): + with open(filename, "w") as f: + yaml.safe_dump(data, f) + + +def load_yml(yml): + return munchify(yaml.safe_load(yml)) + + +def dump_json(data, filename): + with open(filename, "w") as f: + f.write(json.dumps(data, indent=2, sort_keys=True)) + + +def load_json(filename): + with open(filename, "r") as f: + try: + return munchify(json.load(f)) + except Exception as exc: + raise Exception(f"could not load file: {filename}: {exc}") + + +def main(): + def _common_args(parser): + parser.add_argument("-m", "--manifest", type=str, default="", help="enable debug mode") + parser.add_argument("--debug", action="store_true", help="enable debug mode") + # + parser = argparse.ArgumentParser(description="Browse benchmark results", prog="bm") + _common_args(parser) + subparsers = parser.add_subparsers() + # + sp = subparsers.add_parser("create", help="create benchmark collection") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("filename", type=str, help="the YAML file with the benchmark specs") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("meta", help="get the required meta-information: cpu info, commit data") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("cmakecache", type=str, help="the path to the CMakeCache.txt file used to build the benchmark binaries") + sp.add_argument("build_type", type=str, help="the build type, eg Release Debug MinSizeRel RelWithDebInfo") + # + sp = subparsers.add_parser("add", help="add benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("serve", help="serve benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + sp.add_argument("-H", "--host", type=str, default="localhost", help="host. default=%(default)s") + sp.add_argument("-p", "--port", type=int, default=8000, help="port. default=%(default)s") + # + sp = subparsers.add_parser("export", help="export static html") + sp.set_defaults(func=freeze) + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + _common_args(sp) + # + sp = subparsers.add_parser("deps", help="install server dependencies") + sp.set_defaults(func=lambda _: download_deps()) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + _common_args(sp) + # + args = parser.parse_args(sys.argv[1:] if len(sys.argv) > 1 else ["serve"]) + if args.debug: + log(args) + args.func(args) + + +def get_manifest(args): + bmdir = os.path.abspath(args.bmdir) + if not args.manifest: + manifest_yml = os.path.join(bmdir, "manifest.yml") + else: + if not os.path.isabs(args.manifest): + manifest_yml = os.path.join(os.getcwd(), args.manifest) + manifest_json = os.path.join(os.path.dirname(manifest.yml), "manifest.json") + manifest = load_yml_file(manifest_yml) + dump_json(manifest, manifest_json) + return manifest + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +app = flask.Flask(__name__, + template_folder='template') + + +def _setup_app(args): + def _s(prop, val): + assert not hasattr(app, prop), prop + setattr(app, prop, val) + _s('args', args) + _s('manifest', get_manifest(args)) + if args.debug: + app.config["DEBUG"] = True + + +def freeze(args): + "https://pythonhosted.org/Frozen-Flask/" + from flask_frozen import Freezer + _setup_app(args) + freezer = Freezer(app) + freezer.freeze(debug=args.debug) + + +def serve(args): + _setup_app(args) + app.run(host=args.host, port=args.port, debug=args.debug) + + [email protected]("/") +def home(): + log("requested home") + return render_template("index.html") + + [email protected]("/<path>") +def other_(path): + path = escape(path) + d = app.args.bmdir + log("requested other path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path) + + [email protected]("/static/<path>") +def static_(path): + path = escape(path) + d = os.path.join(app.args.bmdir, "static") + log("requested static path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path, cache_timeout=1) # timeout in seconds + + [email protected]("/bm/<commit>/<run>/<resultjson>") +def bm_(commit, run, resultjson): + commit = escape(commit) + run = escape(run) + resultjson = escape(resultjson) + d = os.path.join(app.args.bmdir, "runs", commit, run) + log("requested result:", os.path.join(d, resultjson)) + return send_from_directory(d, resultjson, cache_timeout=1) # timeout in seconds + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def download_deps(): + deps = [ + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/ui/1.12.1/jquery-ui.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css", + "https://www.chartjs.org/dist/2.9.1/Chart.min.js", + #("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.css", "highlight.github.css"), + ("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.min.css", "highlight.github.min.css"), + #"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.js", + "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.min.js", + ] + for src in deps: + if type(src) == str: + base = os.path.basename(src) + else: + src, base = src + dst = f"{os.getcwd()}/static/{base}" + download_url(src, dst) + + +def download_url(url, dst): + log("download url:", url, "--->", dst) + req = requests.get(url, stream=True) + if req.status_code == 200: + sz = 0 + with open(dst, 'wb') as f: + for chunk in req: + f.write(chunk) + sz += len(chunk) + log(f"........ finished: {sz}B") + else: + log(f" error:", req.status_code, url) + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkCollection: + + @staticmethod + def create_new(args): + dir = args.target + filename = os.path.join(dir, "bm.yml") + manifest = os.path.join(dir, "manifest.yml") + if not os.path.exists(dir): + os.makedirs(dir) + shutil.copyfile(args.filename, filename) + dump_yml(load_yml("""{runs: {}, bm: {}}"""), manifest) + return __class__(dir) + + def __init__(self, dir): + if not os.path.exists(dir): + raise Exception(f"not found: {dir}") + self.dir = os.path.abspath(dir) + self.runs_dir = os.path.join(self.dir, "runs") + self.manifest = os.path.join(self.dir, "manifest.yml") + self.filename = os.path.join(self.dir, "bm.yml") + self.specs = munchify(load_yml_file(self.filename)) + self.manif = munchify(load_yml_file(self.manifest)) + + def add(self, results_dir): + results_dir = os.path.abspath(results_dir) + dst_dir, meta = self._read_run(results_dir) + self._add_run(results_dir, dst_dir, meta) + dump_yml(self.manif, self.manifest) + + def _read_run(self, results_dir): + log("adding run...") + id = f"{len(self.manif.runs.keys()):05d}" + log(f"adding run: id={id}") + meta = ResultMeta.load(results_dir) + dst_dir = os.path.join(self.runs_dir, meta.name) + return dst_dir, meta + + def _add_run(self, results_dir, dst_dir, meta): + cats = self._add_meta_categories(meta) + for filename in ("meta.yml", + "CMakeCCompiler.cmake", + "CMakeCXXCompiler.cmake", + "CMakeSystem.cmake", + "compile_commands.json"): + filename = os.path.join(results_dir, filename) + if os.path.exists(filename): + copy_file_to_dir(filename, dst_dir) + else: + if not filename.endswith("compile_commands.json"): + raise Exception(f"wtf???? {filename}") + for name, specs in self.specs.bm.items(): + if not hasattr(specs, 'variants'): + filename = chk(f"{results_dir}/{name}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(name, specs, meta) + else: + for t in specs.variants: + tname = f"{name}-{t}" + filename = chk(f"{results_dir}/{tname}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(tname, specs, meta) + + def _add_bm_run(self, name, specs, meta): + if name not in self.manif.bm.keys(): + self.manif.bm[name] = Munch(specs=specs, entries=[]) + entry = self.manif.bm[name] + entry.specs = specs + if meta.name not in entry.entries: + entry.entries.append(meta.name) + + def _add_meta_categories(self, meta): + run = Munch() + for catname in ('commit', 'cpu', 'system', 'build'): + meta_item = getattr(meta, catname) + self._add_item_to_category(meta.name, catname, meta_item) + run[catname] = meta_item.storage_id + # build specs are too verbose; remove them + self.manif.build[meta.build.storage_id].specs = Munch() + self.manif.runs[meta.name] = run + + def _add_item_to_category(self, run, category_name, item): + if not hasattr(self.manif, category_name): + setattr(self.manif, category_name, Munch()) + category = getattr(self.manif, category_name) + if item.storage_id not in category.keys(): + category[item.storage_id] = Munch(specs=item, entries=[]) + entry = category[item.storage_id] + entry.specs = item + if run not in entry.entries: + entry.entries.append(run) + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class ResultMeta(Munch): + + def __init__(self, results_dir, cmakecache, build_type): + super().__init__(self) + self.date = __class__.get_date() + self.commit = __class__.get_commit(results_dir) + self.cpu = __class__.get_cpu_info() + self.system = __class__.get_sys_info() + self.build = __class__.get_build_info(cmakecache, build_type) + self.name = self._get_name() + + @staticmethod + def load(results_dir): + results_dir = os.path.join(os.path.abspath(results_dir), "meta.yml") + data = load_yml_file(results_dir) + return munchify(data) + + def save(self, results_dir): + out = os.path.join(results_dir, "meta.yml") + log("saving meta:", out) + dump_yml(self, out) + self.build.save(results_dir) + + @staticmethod + def get_date(): + import datetime + now = datetime.datetime.now() + return now.strftime("%Y%m%d-%H%M%S") + + def _get_name(self): + commit = self.commit.storage_name + cpu = self.cpu.storage_name + sys = self.system.storage_name + build = self.build.storage_name + name = f"{commit}/{cpu}-{sys}-{build}" + return name + + @staticmethod + def get_commit(results_dir): + import git + repo = git.Repo(results_dir, search_parent_directories=True) + commit = repo.head.commit + commit = {p: str(getattr(commit, p)) + for p in ('message', 'summary', 'name_rev', + 'author', + 'authored_datetime', + 'committer', + 'committed_datetime',)} + commit = Munch(commit) + commit.message = commit.message.strip() + commit.sha1 = commit.name_rev[:7] + spl = commit.authored_datetime.split(" ") + date = re.sub(r'-', '', spl[0]) + time = re.sub(r'(\d+):(\d+):(\d+).*', r'\1\2\3', spl[1]) + commit.storage_id = commit.sha1 + commit.storage_name = f"git{date}_{time}-{commit.sha1}" + return commit + + @staticmethod + def get_cpu_info(): + import cpuinfo + nfo = cpuinfo.get_cpu_info() + nfo = Munch(nfo) + for a in ('cpu_version', 'cpu_version_string', 'python_version'): + if hasattr(nfo, a): + delattr(nfo, a) + for a in ('arch_string_raw', 'brand_raw', 'hardware_raw', 'vendor_id_raw'): + if not hasattr(nfo, a): + setattr(nfo, a, '') + nfo.storage_id = myhash( + nfo.arch_string_raw, nfo.brand_raw, nfo.hardware_raw, nfo.vendor_id_raw, + nfo.arch, nfo.bits, nfo.count, nfo.family, nfo.model, nfo.stepping, + ",".join(nfo.flags), nfo.hz_advertised_friendly, + nfo.l2_cache_associativity, + nfo.l2_cache_line_size, + nfo.l2_cache_size, + nfo.l3_cache_size, + *optionals('l1_data_cache_size', 'l1_instruction_cache_size') + ) + nfo.storage_name = f"{nfo.arch.lower()}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_sys_info(): + import platform + uname = platform.uname() + nfo = Munch( + sys_platform=sys.platform, + sys=platform.system(), + uname=Munch( + machine=uname.machine, + node=uname.node, + release=uname.release, + system=uname.system, + version=uname.version, + ) + ) + nfo.storage_id = myhash( + nfo.sys_platform, + nfo.uname.machine, + ) + nfo.storage_name = f"{nfo.sys_platform}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_build_info(cmakecache_txt, buildtype): + nfo = CMakeCache(cmakecache_txt) + def _btflags(name): + return (getattr(nfo, name), getattr(nfo, f"{name}_{buildtype.upper()}")) + nfo.storage_id = myhash( + buildtype, + nfo.CMAKE_CXX_COMPILER_ID, + nfo.CMAKE_CXX_COMPILER_VERSION, + nfo.CMAKE_CXX_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_CXX_COMPILER_ABI, + nfo.CMAKE_CXX_SIZEOF_DATA_PTR, + nfo.CMAKE_C_COMPILER_ID, + nfo.CMAKE_C_COMPILER_VERSION, + nfo.CMAKE_C_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_C_COMPILER_ABI, + nfo.CMAKE_C_SIZEOF_DATA_PTR, + *_btflags("CMAKE_CXX_FLAGS"), + *_btflags("CMAKE_C_FLAGS"), + *_btflags("CMAKE_STATIC_LINKER_FLAGS"), + *_btflags("CMAKE_SHARED_LINKER_FLAGS"), + ) + # + ccname = nfo.CMAKE_CXX_COMPILER_ID.lower() + if ccname == "gnu": + ccname = "gcc" + ccname += nfo.CMAKE_CXX_COMPILER_VERSION.lower() + # + if nfo.CMAKE_C_SIZEOF_DATA_PTR == "4": + bits = "32bit" + elif nfo.CMAKE_C_SIZEOF_DATA_PTR == "8": + bits = "64bit" + else: + raise Exception("unknown architecture") + # + nfo.storage_name = f"{bits}_{buildtype}_{ccname}_{nfo.storage_id}" + return nfo + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class CMakeCache(Munch): + + def __init__(self, cmakecache_txt): + import glob + for line in iter_cmake_lines(cmakecache_txt): + spl = line.split("=") + if len(spl) < 2: + continue + k, ty = spl[0].split(":") + v = "=".join(spl[1:]).strip() + setattr(self, k, v) + bdir = os.path.dirname(os.path.abspath(cmakecache_txt)) + self._c_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCCompiler.cmake"))[-1] # get the last + self._cxx_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCXXCompiler.cmake"))[-1] # get the last + self._system_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeSystem.cmake"))[-1] # get the last + self._load_cmake_file(self._c_compiler_file) + self._load_cmake_file(self._cxx_compiler_file) + ccomfile = f"{bdir}/compile_commands.json" + self._compile_commands_file = ccomfile if os.path.exists(ccomfile) else None + + def _load_cmake_file(self, filename): + for line in iter_cmake_lines(filename): + if not line.startswith("set("): + continue + k = re.sub(r"set\((.*)\ +(.*)\)", r"\1", line) + v = re.sub(r"set\((.*)\ +(.*)\)", r"\2", line) + v = v.strip('"').strip("'").strip() + setattr(self, k, v) + + def save(self, results_dir): + copy_file_to_dir(self._c_compiler_file, results_dir) + copy_file_to_dir(self._cxx_compiler_file, results_dir) + copy_file_to_dir(self._system_file, results_dir) + if self._compile_commands_file is not None: + copy_file_to_dir(self._compile_commands_file, results_dir) + + +def iter_cmake_lines(filename): + with open(filename) as f: + for line in f.readlines(): + line = line.strip() + if line.startswith("#") or line.startswith("//") or len(line) == 0: + continue + yield line + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkRun(Munch): + "results of an individual run" + + def __init__(self, json_file, meta_class): + props = load_json(json_file) + setattr(self, "filename", json_file) + assert hasattr(props, "context") + assert hasattr(props, "benchmarks") + super().__init__(**props) + for e in self.benchmarks: + setattr(e, 'meta', meta_class(e.name)) + setattr(self, 'property_names', ( + 'meta', + 'shorttitle', + 'name', + 'run_name', + 'run_type', + 'repetitions', + 'repetition_index', + 'repetition_index', + 'threads', + 'iterations', + 'real_time', + 'cpu_time', + 'time_unit', + 'bytes_per_second', + 'items_per_second', + 'counters', + )) + + @property + def first(self): + return self.benchmarks[0] + + @property + def entries(self): + for entry in self.benchmarks: + yield entry + + @property + def meta(self): + for entry in self.benchmarks: + yield entry.meta + + @property + def filtered_names(self): + for entry in self.benchmarks: + yield entry.meta.shorttitle + + @property + def names(self): + for entry in self.benchmarks: + yield entry.name + + @property + def run_names(self): + for entry in self.benchmarks: + yield entry.run_name + + @property + def run_types(self): + for entry in self.benchmarks: + yield entry.run_type + + @property + def repetitions(self): + for entry in self.benchmarks: + yield entry.repetitions + + @property + def repetition_indices(self): + for entry in self.benchmarks: + yield entry.repetition_index + + @property + def threads(self): + for entry in self.benchmarks: + yield entry.threads + + @property + def iterations(self): + for entry in self.benchmarks: + yield entry.iterations + + @property + def real_time(self): + for entry in self.benchmarks: + yield entry.real_time + + @property + def cpu_time(self): + for entry in self.benchmarks: + yield entry.cpu_time + + @property + def time_unit(self): + for entry in self.benchmarks: + yield entry.time_unit + + @property + def bytes_per_second(self): + for entry in self.benchmarks: + yield entry.bytes_per_second + + @property + def items_per_second(self): + for entry in self.benchmarks: + yield entry.items_per_second + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkPanel: + + def __init__(self, runs, bm_meta_cls=None): + self.runs = [BenchmarkRun(a, bm_meta_cls) for a in runs] + + def plot_bars(self, title): + plot_benchmarks_as_lines(title, *self.runs) + + + def plot_all_lines(self, title): + plot_benchmarks_as_lines(title, *self.runs, + transform=lambda r: r.meta.num_pixels, + line_title_transform=lambda r: r.meta.shortname) + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + + +def plot_benchmarks_as_bars(title, *bm, transform=None): + from bokeh.models import ColumnDataSource, FactorRange + from bokeh.plotting import figure, show + from bokeh.transform import factor_cmap + pass + + + + +def plot_benchmarks_as_lines(title, *bm, transform=None, + line_title_transform=None, + logx=True, logy=True): + import bokeh + from bokeh.plotting import figure, output_file, show + from bokeh.palettes import Dark2_5 as palette + from bokeh.layouts import row, column + from bokeh.models import (Legend, LegendItem, CheckboxGroup, CustomJS, Div, + RadioGroup, Toggle, + ColumnDataSource, DataTable, TableColumn) + from bokeh.models.markers import marker_types + import itertools + # + ids = entry_ids(*bm, transform=transform) + colors = itertools.cycle(palette) + markers = itertools.cycle(marker_types) + p = figure(title=title, + x_axis_type="log" if logx else "linear", + y_axis_type="log" if logy else "linear", + #background_fill_color="#fafafa", + plot_width=1000, + x_axis_label="Number of pixels", + y_axis_label="Throughput (MB/s)", + ) + p.toolbar.autohide = True + #p.toolbar.active_inspect = [hover_tool, crosshair_tool] + p.toolbar.active_drag = "auto" + p.toolbar.active_scroll = "auto" + # + def dft(v): return v if v else (lambda n: n) + tr = dft(transform) + lttr = dft(line_title_transform) + # + for results in bm: + x = [ids[name] for name in results.names] + y = [bps/1e6 for bps in results.bytes_per_second] + c = next(colors) + marker = next(markers) + next(markers) # advance two + line_name = lttr(results.first) + #legends.append(LegendItem(name=c, label=line_name)) + p.scatter(x, y, marker=marker, size=8, color=c, legend_label=line_name) + p.line(x, y, + color=c, alpha=0.9, + #muted_color=c, muted_alpha=0.05, + legend_label=line_name) + p.legend.click_policy = "hide" + p.legend.label_text_font_size = "10px" + # + def input_title(title): + return Div(text=f"<h3>{title}</h3>") + inputs = [] + first = bm[0].first.meta + for k, g in first.checkbox_groups().items(): + cb = CheckboxGroup(labels=[str(v) for v in g], + active=[i for i in range(len(g))], + inline=True) + inputs.append(input_title(k)) + inputs.append(cb) + # + # https://github.com/bokeh/bokeh/blob/branch-2.3/examples/app/export_csv/main.py + x_axis_values = [f"{m.num_pixels}px" for m in bm[0].meta] + table_sources = [] + for i, px in enumerate(x_axis_values): + c = ColumnDataSource(data={ + 'name': [nth(results.filtered_names, i) for results in bm], + 'bytes_per_second': [nth(results.bytes_per_second, i) for results in bm], + 'items_per_second': [nth(results.items_per_second, i) for results in bm], + 'cpu_time': [nth(results.real_time, i) for results in bm], + 'real_time': [nth(results.real_time, i) for results in bm], + 'iterations': [nth(results.iterations, i) for results in bm], + 'threads': [nth(results.threads, i) for results in bm], + }) + table_sources.append(c) + selected_x_index = 8 # FIXME (currently 2000 pixels) + table_source = copy.deepcopy(table_sources[selected_x_index]) + relvalues = Toggle(label="Table: Relative values") + px_title = input_title("Table: number of pixels") + px_radiogroup = RadioGroup(labels=x_axis_values, active=selected_x_index) + table_inputs = [relvalues, px_title, px_radiogroup] + # + table_cols = [ + TableColumn(field='name', title='Name'), + TableColumn(field='bytes_per_second', title='Bytes/second'), + TableColumn(field='items_per_second', title='Items/second'), + TableColumn(field='cpu_time', title='CPU time'), + TableColumn(field='real_time', title='Real time'), + TableColumn(field='iterations', title='Iterations'), + TableColumn(field='threads', title='Threads'), + ] + data_table = DataTable(source=table_source, columns=table_cols, width=1200) + callback = CustomJS(args=dict( + radiogroup=px_radiogroup, + source=table_source, + table=table_sources + ), code=""" + console.log(`active=${radiogroup.active}`); + /*source.data=table[radiogroup.active];*/ + var nrows = source.data['name'].length; + var ts = table[radiogroup.active].data; + var names = ["name", "bytes_per_second", "items_per_second", "cpu_time", "real_time", "iterations", "threads"]; + var ncols = names.length; + console.log(`names=${names} nrows=${nrows} ncols=${ncols}`); + for(var i = 0; i < nrows; i++) { + for(var j = 0; j < ncols; ++j) { + var name = names[j]; + /*console.log(`i=${i} j=${j} name=${name}`);*/ + source.data[name][i] = ts[name][i]; + } + } + source.change.emit(); + """) + px_radiogroup.js_on_change('active', callback) + # lambda attr, old, new: log(f"attr={attr} old={old} new={new} active={px_radiogroup.active}")) + # + layout = column( + row(column(*inputs), p), + row(column(*table_inputs), data_table)) + show(layout) + + +def chain(*iterables): + for it in iterables: + for elm in it: + yield elm + + +def entry_ids(*bm, transform=None): + ids = {} + curr = 0 + for results in bm: + log(os.path.basename(results.filename), "------------------------------") + for entry in results.entries: + log(entry.name) + if transform is not None: + ids[entry.name] = transform(entry) + else: + if ids.get(entry.name) is None: + ids[entry.name] = curr + curr += 1 + return ids + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def add_results(args): + log("adding results:", args.results) + col = BenchmarkCollection(args.target) + col.add(args.results) + + +def add_meta(args): + log("adding bm run metadata to results dir:", args.results) + meta = ResultMeta(results_dir=args.results, + cmakecache=args.cmakecache, + build_type=args.build_type) + meta.save(args.results) + log("adding bm run metadata to results dir: success!") + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +import typing +import enum + + +class _enum(enum.Enum): + def __str__(self): + return str(self.name) + @classmethod + def err_unknown(cls, s): + raise Exception(f"unknown {__class__.__name__}: {s}") + + +class MatrixOrder(_enum): + row_major = "row_major" + col_major = "col_major" + @property + def short(self): + return "rm" if self is MatrixOrder.row_major else "cm" + @classmethod + def make(cls, s): + try: + return {"rm": cls.row_major, "cm": cls.col_major}[s] + except: + cls.err_unknown(s) + + +class MatrixLayout(_enum): + compact = "compact" + strided = "strided" + @classmethod + def make(cls, s): + try: + return cls[s] + except: + cls.err_unknown(s) + + +class DimensionBinding(_enum): + compile_time = "compile_time" + run_time = "run_time" + @property + def short(self): + return "ct" if self is DimensionBinding.compile_time else "rt" + @classmethod + def make(cls, s): + try: + return {"ct": cls.compile_time, "rt": cls.run_time}[s] + except: + cls.err_unknown(s) + + +class MultType(_enum): + naive = "naive" + avx2 = "avx2" + avx2_unroll2 = "avx2_unroll2" + avx2_unroll4 = "avx2_unroll4" + avx2_unroll8 = "avx2_unroll8" + @classmethod + def make(cls, s): + try: + s = s.replace("dotprod_", "").replace("_naive", "") + return cls[s] + except: + cls.err_unknown(s) + + +class MatrixMult(typing.NamedTuple): + title: str + num_pixels: int + num_channels: int + num_features: int + mult_type: MultType + layout: MatrixLayout + dim_binding: DimensionBinding + ret_order: MatrixOrder + lhs_order: MatrixOrder + rhs_order: MatrixOrder + + @classmethod + def make(cls, bm_title: str): + # eg: + # mult_naive_strided_ct_rm_cmcm<250, 8, 16> + # mult_naive_compact_rt_rm_rmrm/4000/8/16 + rxline = r'mult_(.*)[</](\d+)(?:/|, )(\d+)(?:/|, )(\d+).*' + rxcase = r"(.*)_(compact|strided)_(ct|rt)_(rm|cm)_(rm|cm)(rm|cm)" + case = re.sub(rxline, r'\1', bm_title) + return cls( + title=case, + num_pixels=int(re.sub(rxline, r'\2', bm_title)), + num_channels=int(re.sub(rxline, r'\3', bm_title)), + num_features=int(re.sub(rxline, r'\4', bm_title)), + mult_type=MultType.make(re.sub(rxcase, r'\1', case)), + layout=MatrixLayout.make(re.sub(rxcase, r'\2', case)), + dim_binding=DimensionBinding.make(re.sub(rxcase, r'\3', case)), + ret_order=MatrixOrder.make(re.sub(rxcase, r'\4', case)), + lhs_order=MatrixOrder.make(re.sub(rxcase, r'\5', case)), + rhs_order=MatrixOrder.make(re.sub(rxcase, r'\6', case)) + ) + + def comparison_axes(self): + return ('num_pixels', 'num_channels', 'num_features') + + def checkbox_groups(self): + return { + 'multiplication method': [t for t in MultType], + 'layout': [t for t in MatrixLayout], + 'dimension': [d for d in DimensionBinding], + 'return matrix ordering': [o for o in MatrixOrder], + 'lhs matrix ordering': [o for o in MatrixOrder], + 'rhs matrix ordering': [o for o in MatrixOrder], + } + + @property + def matrix_size(self): + return self.num_pixels * self.num_channels + + @property + def classifier_size(self): + return self.num_channels * self.num_features + + @property + def shortname(self): + m = self + return f"{m.mult_type}/{m.layout}/{m.dim_binding.short}_{m.ret_order.short}_{m.lhs_order.short}{m.rhs_order.short}" + + @property + def shortparams(self): + m = self + return f"{m.num_pixels:04d}px{m.num_channels:02d}ch{m.num_features:02d}ft" + + @property + def shorttitle(self): + return f"{self.shortname}/{self.shortparams}" + + +def _test(): + def expect(v_, attr, val): + var = getattr(v_, attr) + if var != val: + raise Exception(f"{attr}: expected={val} actual={var}") + # + v = MatrixMult.make("mult_naive_strided_ct_rm_cmcm<250, 8, 16>") + expect(v, 'title', 'naive_strided_ct_rm_cmcm') + expect(v, 'num_pixels', 250) + expect(v, 'num_channels', 8) + expect(v, 'num_features', 16) + expect(v, 'mult_type', MultType.naive) + expect(v, 'layout', MatrixLayout.strided) + expect(v, 'dim_binding', DimensionBinding.compile_time) + expect(v, 'ret_order', MatrixOrder.row_major) + expect(v, 'lhs_order', MatrixOrder.col_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + v = MatrixMult.make("mult_dotprod_avx2_compact_rt_cm_rmcm/4000/16/8") + expect(v, 'title', 'dotprod_avx2_compact_rt_cm_rmcm') + expect(v, 'num_pixels', 4000) + expect(v, 'num_channels', 16) + expect(v, 'num_features', 8) + expect(v, 'mult_type', MultType.avx2) + expect(v, 'layout', MatrixLayout.compact) + expect(v, 'dim_binding', DimensionBinding.run_time) + expect(v, 'ret_order', MatrixOrder.col_major) + expect(v, 'lhs_order', MatrixOrder.row_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + +_test() + + + +def formatMBps(value): + return value / 1e6 + + + +if __name__ == '__main__': + bms = sorted(sys.argv[2:]) + log(bms) + bms = BenchmarkPanel(bms, bm_meta_cls=MatrixMult.make) + fm = bms.runs[0].first.meta + title = f"Classifier multiplication, {fm.num_channels} channels, {fm.num_features} features: throughput (MB/s)" + bms.plot_all_lines(title) + exit() + main() diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_mintm.py b/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_mintm.py new file mode 100644 index 000000000..3db4175cb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/example_mintm.py @@ -0,0 +1,1061 @@ +import os +import sys +import argparse +import copy +import requests +import flask +import json +import re +import yaml +import shutil +import mmh3 +from itertools import islice + +from munch import Munch, munchify +from flask import render_template, redirect, url_for, send_from_directory +from markupsafe import escape + + +def log(*args, **kwargs): + print(*args, **kwargs, flush=True) + + +def myhash_combine(curr, value): + return curr ^ (value + 0x9e3779b9 + (curr<<6) + (curr>>2)) + + +def nth(iterable, n, default=None): + "Returns the nth item or a default value" + return next(islice(iterable, n, None), default) + + +def optionals(obj, *attrs): + ret = [] + for attr in attrs: + if not hasattr(obj, attr): + log("attr not present:", attr) + continue + ret.append(getattr(obj, attr)) + return ret + + +def myhash(*args): + h = 137597 + for a in args: + if isinstance(a, str): + if a == "": + continue + b = bytes(a, "utf8") + else: + b = bytes(a) + hb = mmh3.hash(b, signed=False) + h = myhash_combine(h, hb) + s = hex(h) + return s[2:min(10, len(s))] + + +def copy_file_to_dir(file, dir): + dir = os.path.abspath(dir) + src = os.path.abspath(file) + dst = f"{dir}/{os.path.basename(src)}" + if not os.path.exists(dir): + os.makedirs(dir) + if os.path.exists(dst): + os.remove(dst) + log("copy:", src, "-->", dst) + shutil.copy(src, dst) + return dst + + +def chk(f): + log(f"looking for file:", f) + assert os.path.exists(f), f + return f + + +def load_yml_file(filename): + if not os.path.exists(filename): + raise Exception(f"not found: {filename}") + with open(filename) as f: + return load_yml(f.read()) + + +def dump_yml(data, filename): + with open(filename, "w") as f: + yaml.safe_dump(data, f) + + +def load_yml(yml): + return munchify(yaml.safe_load(yml)) + + +def dump_json(data, filename): + with open(filename, "w") as f: + f.write(json.dumps(data, indent=2, sort_keys=True)) + + +def load_json(filename): + with open(filename, "r") as f: + try: + return munchify(json.load(f)) + except Exception as exc: + raise Exception(f"could not load file: {filename}: {exc}") + + +def main(): + def _common_args(parser): + parser.add_argument("-m", "--manifest", type=str, default="", help="enable debug mode") + parser.add_argument("--debug", action="store_true", help="enable debug mode") + # + parser = argparse.ArgumentParser(description="Browse benchmark results", prog="bm") + _common_args(parser) + subparsers = parser.add_subparsers() + # + sp = subparsers.add_parser("create", help="create benchmark collection") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("filename", type=str, help="the YAML file with the benchmark specs") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("meta", help="get the required meta-information: cpu info, commit data") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("cmakecache", type=str, help="the path to the CMakeCache.txt file used to build the benchmark binaries") + sp.add_argument("build_type", type=str, help="the build type, eg Release Debug MinSizeRel RelWithDebInfo") + # + sp = subparsers.add_parser("add", help="add benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("results", type=str, help="the directory with the results") + sp.add_argument("target", type=str, help="the directory to store the results") + # + sp = subparsers.add_parser("serve", help="serve benchmark results") + _common_args(sp) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + sp.add_argument("-H", "--host", type=str, default="localhost", help="host. default=%(default)s") + sp.add_argument("-p", "--port", type=int, default=8000, help="port. default=%(default)s") + # + sp = subparsers.add_parser("export", help="export static html") + sp.set_defaults(func=freeze) + sp.add_argument("bmdir", type=os.path.abspath, default=os.getcwd(), help="the directory with the results. default=.") + _common_args(sp) + # + sp = subparsers.add_parser("deps", help="install server dependencies") + sp.set_defaults(func=lambda _: download_deps()) + sp.add_argument("--debug", action="store_true", help="enable debug mode") + _common_args(sp) + # + args = parser.parse_args(sys.argv[1:] if len(sys.argv) > 1 else ["serve"]) + if args.debug: + log(args) + args.func(args) + + +def get_manifest(args): + bmdir = os.path.abspath(args.bmdir) + if not args.manifest: + manifest_yml = os.path.join(bmdir, "manifest.yml") + else: + if not os.path.isabs(args.manifest): + manifest_yml = os.path.join(os.getcwd(), args.manifest) + manifest_json = os.path.join(os.path.dirname(manifest.yml), "manifest.json") + manifest = load_yml_file(manifest_yml) + dump_json(manifest, manifest_json) + return manifest + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +app = flask.Flask(__name__, + template_folder='template') + + +def _setup_app(args): + def _s(prop, val): + assert not hasattr(app, prop), prop + setattr(app, prop, val) + _s('args', args) + _s('manifest', get_manifest(args)) + if args.debug: + app.config["DEBUG"] = True + + +def freeze(args): + "https://pythonhosted.org/Frozen-Flask/" + from flask_frozen import Freezer + _setup_app(args) + freezer = Freezer(app) + freezer.freeze(debug=args.debug) + + +def serve(args): + _setup_app(args) + app.run(host=args.host, port=args.port, debug=args.debug) + + [email protected]("/") +def home(): + log("requested home") + return render_template("index.html") + + [email protected]("/<path>") +def other_(path): + path = escape(path) + d = app.args.bmdir + log("requested other path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path) + + [email protected]("/static/<path>") +def static_(path): + path = escape(path) + d = os.path.join(app.args.bmdir, "static") + log("requested static path:", path, "---", os.path.join(d, path)) + return send_from_directory(d, path, cache_timeout=1) # timeout in seconds + + [email protected]("/bm/<commit>/<run>/<resultjson>") +def bm_(commit, run, resultjson): + commit = escape(commit) + run = escape(run) + resultjson = escape(resultjson) + d = os.path.join(app.args.bmdir, "runs", commit, run) + log("requested result:", os.path.join(d, resultjson)) + return send_from_directory(d, resultjson, cache_timeout=1) # timeout in seconds + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def download_deps(): + deps = [ + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/jquery-3.3.1.js", + "https://code.jquery.com/ui/1.12.1/jquery-ui.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.js", + "https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.css", + "https://cdn.datatables.net/1.10.20/css/jquery.dataTables.min.css", + "https://www.chartjs.org/dist/2.9.1/Chart.min.js", + #("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.css", "highlight.github.css"), + ("https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/styles/github.min.css", "highlight.github.min.css"), + #"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.js", + "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.3.2/highlight.min.js", + ] + for src in deps: + if type(src) == str: + base = os.path.basename(src) + else: + src, base = src + dst = f"{os.getcwd()}/static/{base}" + download_url(src, dst) + + +def download_url(url, dst): + log("download url:", url, "--->", dst) + req = requests.get(url, stream=True) + if req.status_code == 200: + sz = 0 + with open(dst, 'wb') as f: + for chunk in req: + f.write(chunk) + sz += len(chunk) + log(f"........ finished: {sz}B") + else: + log(f" error:", req.status_code, url) + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkCollection: + + @staticmethod + def create_new(args): + dir = args.target + filename = os.path.join(dir, "bm.yml") + manifest = os.path.join(dir, "manifest.yml") + if not os.path.exists(dir): + os.makedirs(dir) + shutil.copyfile(args.filename, filename) + dump_yml(load_yml("""{runs: {}, bm: {}}"""), manifest) + return __class__(dir) + + def __init__(self, dir): + if not os.path.exists(dir): + raise Exception(f"not found: {dir}") + self.dir = os.path.abspath(dir) + self.runs_dir = os.path.join(self.dir, "runs") + self.manifest = os.path.join(self.dir, "manifest.yml") + self.filename = os.path.join(self.dir, "bm.yml") + self.specs = munchify(load_yml_file(self.filename)) + self.manif = munchify(load_yml_file(self.manifest)) + + def add(self, results_dir): + results_dir = os.path.abspath(results_dir) + dst_dir, meta = self._read_run(results_dir) + self._add_run(results_dir, dst_dir, meta) + dump_yml(self.manif, self.manifest) + + def _read_run(self, results_dir): + log("adding run...") + id = f"{len(self.manif.runs.keys()):05d}" + log(f"adding run: id={id}") + meta = ResultMeta.load(results_dir) + dst_dir = os.path.join(self.runs_dir, meta.name) + return dst_dir, meta + + def _add_run(self, results_dir, dst_dir, meta): + cats = self._add_meta_categories(meta) + for filename in ("meta.yml", + "CMakeCCompiler.cmake", + "CMakeCXXCompiler.cmake", + "CMakeSystem.cmake", + "compile_commands.json"): + filename = os.path.join(results_dir, filename) + if os.path.exists(filename): + copy_file_to_dir(filename, dst_dir) + else: + if not filename.endswith("compile_commands.json"): + raise Exception(f"wtf???? {filename}") + for name, specs in self.specs.bm.items(): + if not hasattr(specs, 'variants'): + filename = chk(f"{results_dir}/{name}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(name, specs, meta) + else: + for t in specs.variants: + tname = f"{name}-{t}" + filename = chk(f"{results_dir}/{tname}.json") + dst = copy_file_to_dir(filename, dst_dir) + self._add_bm_run(tname, specs, meta) + + def _add_bm_run(self, name, specs, meta): + if name not in self.manif.bm.keys(): + self.manif.bm[name] = Munch(specs=specs, entries=[]) + entry = self.manif.bm[name] + entry.specs = specs + if meta.name not in entry.entries: + entry.entries.append(meta.name) + + def _add_meta_categories(self, meta): + run = Munch() + for catname in ('commit', 'cpu', 'system', 'build'): + meta_item = getattr(meta, catname) + self._add_item_to_category(meta.name, catname, meta_item) + run[catname] = meta_item.storage_id + # build specs are too verbose; remove them + self.manif.build[meta.build.storage_id].specs = Munch() + self.manif.runs[meta.name] = run + + def _add_item_to_category(self, run, category_name, item): + if not hasattr(self.manif, category_name): + setattr(self.manif, category_name, Munch()) + category = getattr(self.manif, category_name) + if item.storage_id not in category.keys(): + category[item.storage_id] = Munch(specs=item, entries=[]) + entry = category[item.storage_id] + entry.specs = item + if run not in entry.entries: + entry.entries.append(run) + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class ResultMeta(Munch): + + def __init__(self, results_dir, cmakecache, build_type): + super().__init__(self) + self.date = __class__.get_date() + self.commit = __class__.get_commit(results_dir) + self.cpu = __class__.get_cpu_info() + self.system = __class__.get_sys_info() + self.build = __class__.get_build_info(cmakecache, build_type) + self.name = self._get_name() + + @staticmethod + def load(results_dir): + results_dir = os.path.join(os.path.abspath(results_dir), "meta.yml") + data = load_yml_file(results_dir) + return munchify(data) + + def save(self, results_dir): + out = os.path.join(results_dir, "meta.yml") + log("saving meta:", out) + dump_yml(self, out) + self.build.save(results_dir) + + @staticmethod + def get_date(): + import datetime + now = datetime.datetime.now() + return now.strftime("%Y%m%d-%H%M%S") + + def _get_name(self): + commit = self.commit.storage_name + cpu = self.cpu.storage_name + sys = self.system.storage_name + build = self.build.storage_name + name = f"{commit}/{cpu}-{sys}-{build}" + return name + + @staticmethod + def get_commit(results_dir): + import git + repo = git.Repo(results_dir, search_parent_directories=True) + commit = repo.head.commit + commit = {p: str(getattr(commit, p)) + for p in ('message', 'summary', 'name_rev', + 'author', + 'authored_datetime', + 'committer', + 'committed_datetime',)} + commit = Munch(commit) + commit.message = commit.message.strip() + commit.sha1 = commit.name_rev[:7] + spl = commit.authored_datetime.split(" ") + date = re.sub(r'-', '', spl[0]) + time = re.sub(r'(\d+):(\d+):(\d+).*', r'\1\2\3', spl[1]) + commit.storage_id = commit.sha1 + commit.storage_name = f"git{date}_{time}-{commit.sha1}" + return commit + + @staticmethod + def get_cpu_info(): + import cpuinfo + nfo = cpuinfo.get_cpu_info() + nfo = Munch(nfo) + for a in ('cpu_version', 'cpu_version_string', 'python_version'): + if hasattr(nfo, a): + delattr(nfo, a) + for a in ('arch_string_raw', 'brand_raw', 'hardware_raw', 'vendor_id_raw'): + if not hasattr(nfo, a): + setattr(nfo, a, '') + nfo.storage_id = myhash( + nfo.arch_string_raw, nfo.brand_raw, nfo.hardware_raw, nfo.vendor_id_raw, + nfo.arch, nfo.bits, nfo.count, nfo.family, nfo.model, nfo.stepping, + ",".join(nfo.flags), nfo.hz_advertised_friendly, + nfo.l2_cache_associativity, + nfo.l2_cache_line_size, + nfo.l2_cache_size, + nfo.l3_cache_size, + *optionals('l1_data_cache_size', 'l1_instruction_cache_size') + ) + nfo.storage_name = f"{nfo.arch.lower()}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_sys_info(): + import platform + uname = platform.uname() + nfo = Munch( + sys_platform=sys.platform, + sys=platform.system(), + uname=Munch( + machine=uname.machine, + node=uname.node, + release=uname.release, + system=uname.system, + version=uname.version, + ) + ) + nfo.storage_id = myhash( + nfo.sys_platform, + nfo.uname.machine, + ) + nfo.storage_name = f"{nfo.sys_platform}_{nfo.storage_id}" + return nfo + + @staticmethod + def get_build_info(cmakecache_txt, buildtype): + nfo = CMakeCache(cmakecache_txt) + def _btflags(name): + return (getattr(nfo, name), getattr(nfo, f"{name}_{buildtype.upper()}")) + nfo.storage_id = myhash( + buildtype, + nfo.CMAKE_CXX_COMPILER_ID, + nfo.CMAKE_CXX_COMPILER_VERSION, + nfo.CMAKE_CXX_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_CXX_COMPILER_ABI, + nfo.CMAKE_CXX_SIZEOF_DATA_PTR, + nfo.CMAKE_C_COMPILER_ID, + nfo.CMAKE_C_COMPILER_VERSION, + nfo.CMAKE_C_COMPILER_VERSION_INTERNAL, + nfo.CMAKE_C_COMPILER_ABI, + nfo.CMAKE_C_SIZEOF_DATA_PTR, + *_btflags("CMAKE_CXX_FLAGS"), + *_btflags("CMAKE_C_FLAGS"), + *_btflags("CMAKE_STATIC_LINKER_FLAGS"), + *_btflags("CMAKE_SHARED_LINKER_FLAGS"), + ) + # + ccname = nfo.CMAKE_CXX_COMPILER_ID.lower() + if ccname == "gnu": + ccname = "gcc" + ccname += nfo.CMAKE_CXX_COMPILER_VERSION.lower() + # + if nfo.CMAKE_C_SIZEOF_DATA_PTR == "4": + bits = "32bit" + elif nfo.CMAKE_C_SIZEOF_DATA_PTR == "8": + bits = "64bit" + else: + raise Exception("unknown architecture") + # + nfo.storage_name = f"{bits}_{buildtype}_{ccname}_{nfo.storage_id}" + return nfo + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +class CMakeCache(Munch): + + def __init__(self, cmakecache_txt): + import glob + for line in iter_cmake_lines(cmakecache_txt): + spl = line.split("=") + if len(spl) < 2: + continue + k, ty = spl[0].split(":") + v = "=".join(spl[1:]).strip() + setattr(self, k, v) + bdir = os.path.dirname(os.path.abspath(cmakecache_txt)) + self._c_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCCompiler.cmake"))[-1] # get the last + self._cxx_compiler_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeCXXCompiler.cmake"))[-1] # get the last + self._system_file = sorted(glob.glob(f"{bdir}/CMakeFiles/*/CMakeSystem.cmake"))[-1] # get the last + self._load_cmake_file(self._c_compiler_file) + self._load_cmake_file(self._cxx_compiler_file) + ccomfile = f"{bdir}/compile_commands.json" + self._compile_commands_file = ccomfile if os.path.exists(ccomfile) else None + + def _load_cmake_file(self, filename): + for line in iter_cmake_lines(filename): + if not line.startswith("set("): + continue + k = re.sub(r"set\((.*)\ +(.*)\)", r"\1", line) + v = re.sub(r"set\((.*)\ +(.*)\)", r"\2", line) + v = v.strip('"').strip("'").strip() + setattr(self, k, v) + + def save(self, results_dir): + copy_file_to_dir(self._c_compiler_file, results_dir) + copy_file_to_dir(self._cxx_compiler_file, results_dir) + copy_file_to_dir(self._system_file, results_dir) + if self._compile_commands_file is not None: + copy_file_to_dir(self._compile_commands_file, results_dir) + + +def iter_cmake_lines(filename): + with open(filename) as f: + for line in f.readlines(): + line = line.strip() + if line.startswith("#") or line.startswith("//") or len(line) == 0: + continue + yield line + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkRun(Munch): + "results of an individual run" + + def __init__(self, json_file, meta_class): + props = load_json(json_file) + setattr(self, "filename", json_file) + assert hasattr(props, "context") + assert hasattr(props, "benchmarks") + super().__init__(**props) + for e in self.benchmarks: + setattr(e, 'meta', meta_class(e.name)) + setattr(self, 'property_names', ( + 'meta', + 'shorttitle', + 'name', + 'run_name', + 'run_type', + 'repetitions', + 'repetition_index', + 'repetition_index', + 'threads', + 'iterations', + 'real_time', + 'cpu_time', + 'time_unit', + 'bytes_per_second', + 'items_per_second', + 'counters', + )) + + @property + def first(self): + return self.benchmarks[0] + + @property + def entries(self): + for entry in self.benchmarks: + yield entry + + @property + def meta(self): + for entry in self.benchmarks: + yield entry.meta + + @property + def filtered_names(self): + for entry in self.benchmarks: + yield entry.meta.shorttitle + + @property + def names(self): + for entry in self.benchmarks: + yield entry.name + + @property + def run_names(self): + for entry in self.benchmarks: + yield entry.run_name + + @property + def run_types(self): + for entry in self.benchmarks: + yield entry.run_type + + @property + def repetitions(self): + for entry in self.benchmarks: + yield entry.repetitions + + @property + def repetition_indices(self): + for entry in self.benchmarks: + yield entry.repetition_index + + @property + def threads(self): + for entry in self.benchmarks: + yield entry.threads + + @property + def iterations(self): + for entry in self.benchmarks: + yield entry.iterations + + @property + def real_time(self): + for entry in self.benchmarks: + yield entry.real_time + + @property + def cpu_time(self): + for entry in self.benchmarks: + yield entry.cpu_time + + @property + def time_unit(self): + for entry in self.benchmarks: + yield entry.time_unit + + @property + def bytes_per_second(self): + for entry in self.benchmarks: + yield entry.bytes_per_second + + @property + def items_per_second(self): + for entry in self.benchmarks: + yield entry.items_per_second + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +class BenchmarkPanel: + + def __init__(self, runs, bm_meta_cls=None): + self.runs = [BenchmarkRun(a, bm_meta_cls) for a in runs] + + def plot_bars(self, title): + plot_benchmarks_as_lines(title, *self.runs) + + + def plot_all_lines(self, title): + plot_benchmarks_as_lines(title, *self.runs, + transform=lambda r: r.meta.num_pixels, + line_title_transform=lambda r: r.meta.shortname) + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + + +def plot_benchmarks_as_bars(title, *bm, transform=None): + from bokeh.models import ColumnDataSource, FactorRange + from bokeh.plotting import figure, show + from bokeh.transform import factor_cmap + pass + + + + +def plot_benchmarks_as_lines(title, *bm, transform=None, + line_title_transform=None, + logx=True, logy=True): + import bokeh + from bokeh.plotting import figure, output_file, show + from bokeh.palettes import Dark2_5 as palette + from bokeh.layouts import row, column + from bokeh.models import (Legend, LegendItem, CheckboxGroup, CustomJS, Div, + RadioGroup, Toggle, + ColumnDataSource, DataTable, TableColumn) + from bokeh.models.markers import marker_types + import itertools + # + ids = entry_ids(*bm, transform=transform) + colors = itertools.cycle(palette) + markers = itertools.cycle(marker_types) + p = figure(title=title, + x_axis_type="log" if logx else "linear", + y_axis_type="log" if logy else "linear", + #background_fill_color="#fafafa", + plot_width=1000, + x_axis_label="Number of pixels", + y_axis_label="Throughput (MB/s)", + ) + p.toolbar.autohide = True + #p.toolbar.active_inspect = [hover_tool, crosshair_tool] + p.toolbar.active_drag = "auto" + p.toolbar.active_scroll = "auto" + # + def dft(v): return v if v else (lambda n: n) + tr = dft(transform) + lttr = dft(line_title_transform) + # + for results in bm: + x = [ids[name] for name in results.names] + y = [bps/1e6 for bps in results.bytes_per_second] + c = next(colors) + marker = next(markers) + next(markers) # advance two + line_name = lttr(results.first) + #legends.append(LegendItem(name=c, label=line_name)) + p.scatter(x, y, marker=marker, size=8, color=c, legend_label=line_name) + p.line(x, y, + color=c, alpha=0.9, + #muted_color=c, muted_alpha=0.05, + legend_label=line_name) + p.legend.click_policy = "hide" + p.legend.label_text_font_size = "10px" + # + def input_title(title): + return Div(text=f"<h3>{title}</h3>") + inputs = [] + first = bm[0].first.meta + for k, g in first.checkbox_groups().items(): + cb = CheckboxGroup(labels=[str(v) for v in g], + active=[i for i in range(len(g))], + inline=True) + inputs.append(input_title(k)) + inputs.append(cb) + # + # https://github.com/bokeh/bokeh/blob/branch-2.3/examples/app/export_csv/main.py + x_axis_values = [f"{m.num_pixels}px" for m in bm[0].meta] + table_sources = [] + for i, px in enumerate(x_axis_values): + c = ColumnDataSource(data={ + 'name': [nth(results.filtered_names, i) for results in bm], + 'bytes_per_second': [nth(results.bytes_per_second, i) for results in bm], + 'items_per_second': [nth(results.items_per_second, i) for results in bm], + 'cpu_time': [nth(results.real_time, i) for results in bm], + 'real_time': [nth(results.real_time, i) for results in bm], + 'iterations': [nth(results.iterations, i) for results in bm], + 'threads': [nth(results.threads, i) for results in bm], + }) + table_sources.append(c) + selected_x_index = 8 # FIXME (currently 2000 pixels) + table_source = copy.deepcopy(table_sources[selected_x_index]) + relvalues = Toggle(label="Table: Relative values") + px_title = input_title("Table: number of pixels") + px_radiogroup = RadioGroup(labels=x_axis_values, active=selected_x_index) + table_inputs = [relvalues, px_title, px_radiogroup] + # + table_cols = [ + TableColumn(field='name', title='Name'), + TableColumn(field='bytes_per_second', title='Bytes/second'), + TableColumn(field='items_per_second', title='Items/second'), + TableColumn(field='cpu_time', title='CPU time'), + TableColumn(field='real_time', title='Real time'), + TableColumn(field='iterations', title='Iterations'), + TableColumn(field='threads', title='Threads'), + ] + data_table = DataTable(source=table_source, columns=table_cols, width=1200) + callback = CustomJS(args=dict( + radiogroup=px_radiogroup, + source=table_source, + table=table_sources + ), code=""" + console.log(`active=${radiogroup.active}`); + /*source.data=table[radiogroup.active];*/ + var nrows = source.data['name'].length; + var ts = table[radiogroup.active].data; + var names = ["name", "bytes_per_second", "items_per_second", "cpu_time", "real_time", "iterations", "threads"]; + var ncols = names.length; + console.log(`names=${names} nrows=${nrows} ncols=${ncols}`); + for(var i = 0; i < nrows; i++) { + for(var j = 0; j < ncols; ++j) { + var name = names[j]; + /*console.log(`i=${i} j=${j} name=${name}`);*/ + source.data[name][i] = ts[name][i]; + } + } + source.change.emit(); + """) + px_radiogroup.js_on_change('active', callback) + # lambda attr, old, new: log(f"attr={attr} old={old} new={new} active={px_radiogroup.active}")) + # + layout = column( + row(column(*inputs), p), + row(column(*table_inputs), data_table)) + show(layout) + + +def chain(*iterables): + for it in iterables: + for elm in it: + yield elm + + +def entry_ids(*bm, transform=None): + ids = {} + curr = 0 + for results in bm: + log(os.path.basename(results.filename), "------------------------------") + for entry in results.entries: + log(entry.name) + if transform is not None: + ids[entry.name] = transform(entry) + else: + if ids.get(entry.name) is None: + ids[entry.name] = curr + curr += 1 + return ids + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +def add_results(args): + log("adding results:", args.results) + col = BenchmarkCollection(args.target) + col.add(args.results) + + +def add_meta(args): + log("adding bm run metadata to results dir:", args.results) + meta = ResultMeta(results_dir=args.results, + cmakecache=args.cmakecache, + build_type=args.build_type) + meta.save(args.results) + log("adding bm run metadata to results dir: success!") + + +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ + +import typing +import enum + + +class _enum(enum.Enum): + def __str__(self): + return str(self.name) + @classmethod + def err_unknown(cls, s): + raise Exception(f"unknown {__class__.__name__}: {s}") + + +class MatrixOrder(_enum): + row_major = "row_major" + col_major = "col_major" + @property + def short(self): + return "rm" if self is MatrixOrder.row_major else "cm" + @classmethod + def make(cls, s): + try: + return {"rm": cls.row_major, "cm": cls.col_major}[s] + except: + cls.err_unknown(s) + + +class MatrixLayout(_enum): + compact = "compact" + strided = "strided" + @classmethod + def make(cls, s): + try: + return cls[s] + except: + cls.err_unknown(s) + + +class DimensionBinding(_enum): + compile_time = "compile_time" + run_time = "run_time" + @property + def short(self): + return "ct" if self is DimensionBinding.compile_time else "rt" + @classmethod + def make(cls, s): + try: + return {"ct": cls.compile_time, "rt": cls.run_time}[s] + except: + cls.err_unknown(s) + + +class MultType(_enum): + naive = "naive" + avx2 = "avx2" + avx2_unroll2 = "avx2_unroll2" + avx2_unroll4 = "avx2_unroll4" + avx2_unroll8 = "avx2_unroll8" + @classmethod + def make(cls, s): + try: + s = s.replace("dotprod_", "").replace("_naive", "") + return cls[s] + except: + cls.err_unknown(s) + + +class MatrixMult(typing.NamedTuple): + title: str + num_pixels: int + num_channels: int + num_features: int + mult_type: MultType + layout: MatrixLayout + dim_binding: DimensionBinding + ret_order: MatrixOrder + lhs_order: MatrixOrder + rhs_order: MatrixOrder + + @classmethod + def make(cls, bm_title: str): + # eg: + # mult_naive_strided_ct_rm_cmcm<250, 8, 16> + # mult_naive_compact_rt_rm_rmrm/4000/8/16 + rxline = r'mult_(.*)[</](\d+)(?:/|, )(\d+)(?:/|, )(\d+).*' + rxcase = r"(.*)_(compact|strided)_(ct|rt)_(rm|cm)_(rm|cm)(rm|cm)" + case = re.sub(rxline, r'\1', bm_title) + return cls( + title=case, + num_pixels=int(re.sub(rxline, r'\2', bm_title)), + num_channels=int(re.sub(rxline, r'\3', bm_title)), + num_features=int(re.sub(rxline, r'\4', bm_title)), + mult_type=MultType.make(re.sub(rxcase, r'\1', case)), + layout=MatrixLayout.make(re.sub(rxcase, r'\2', case)), + dim_binding=DimensionBinding.make(re.sub(rxcase, r'\3', case)), + ret_order=MatrixOrder.make(re.sub(rxcase, r'\4', case)), + lhs_order=MatrixOrder.make(re.sub(rxcase, r'\5', case)), + rhs_order=MatrixOrder.make(re.sub(rxcase, r'\6', case)) + ) + + def comparison_axes(self): + return ('num_pixels', 'num_channels', 'num_features') + + def checkbox_groups(self): + return { + 'multiplication method': [t for t in MultType], + 'layout': [t for t in MatrixLayout], + 'dimension': [d for d in DimensionBinding], + 'return matrix ordering': [o for o in MatrixOrder], + 'lhs matrix ordering': [o for o in MatrixOrder], + 'rhs matrix ordering': [o for o in MatrixOrder], + } + + @property + def matrix_size(self): + return self.num_pixels * self.num_channels + + @property + def classifier_size(self): + return self.num_channels * self.num_features + + @property + def shortname(self): + m = self + return f"{m.mult_type}/{m.layout}/{m.dim_binding.short}_{m.ret_order.short}_{m.lhs_order.short}{m.rhs_order.short}" + + @property + def shortparams(self): + m = self + return f"{m.num_pixels:04d}px{m.num_channels:02d}ch{m.num_features:02d}ft" + + @property + def shorttitle(self): + return f"{self.shortname}/{self.shortparams}" + + +def _test(): + def expect(v_, attr, val): + var = getattr(v_, attr) + if var != val: + raise Exception(f"{attr}: expected={val} actual={var}") + # + v = MatrixMult.make("mult_naive_strided_ct_rm_cmcm<250, 8, 16>") + expect(v, 'title', 'naive_strided_ct_rm_cmcm') + expect(v, 'num_pixels', 250) + expect(v, 'num_channels', 8) + expect(v, 'num_features', 16) + expect(v, 'mult_type', MultType.naive) + expect(v, 'layout', MatrixLayout.strided) + expect(v, 'dim_binding', DimensionBinding.compile_time) + expect(v, 'ret_order', MatrixOrder.row_major) + expect(v, 'lhs_order', MatrixOrder.col_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + v = MatrixMult.make("mult_dotprod_avx2_compact_rt_cm_rmcm/4000/16/8") + expect(v, 'title', 'dotprod_avx2_compact_rt_cm_rmcm') + expect(v, 'num_pixels', 4000) + expect(v, 'num_channels', 16) + expect(v, 'num_features', 8) + expect(v, 'mult_type', MultType.avx2) + expect(v, 'layout', MatrixLayout.compact) + expect(v, 'dim_binding', DimensionBinding.run_time) + expect(v, 'ret_order', MatrixOrder.col_major) + expect(v, 'lhs_order', MatrixOrder.row_major) + expect(v, 'rhs_order', MatrixOrder.col_major) + +_test() + + + +def formatMBps(value): + return value / 1e6 + + + +if __name__ == '__main__': + bms = sorted(sys.argv[2:]) + log(bms) + bms = BenchmarkPanel(bms, bm_meta_cls=MatrixMult.make) + fm = bms.runs[0].first.meta + title = f"Classifier multiplication, {fm.num_channels} channels, {fm.num_features} features: throughput (MB/s)" + bms.plot_all_lines(title) + exit() + main() diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/requirements.txt b/thirdparty/ryml/ext/c4core/cmake/bm-xp/requirements.txt new file mode 100644 index 000000000..5a1b395ef --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/requirements.txt @@ -0,0 +1,14 @@ +munch +pyyaml +py-cpuinfo +psutil +gitpython +flask +markupsafe +Frozen-Flask +requests +mmh3 +bokeh<3.0 +selenium +matplotlib +prettytable diff --git a/thirdparty/ryml/ext/c4core/cmake/bm-xp/template/index.html b/thirdparty/ryml/ext/c4core/cmake/bm-xp/template/index.html new file mode 100644 index 000000000..fb52b8ba0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/bm-xp/template/index.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<html> + + <head> + <meta charset="UTF-8"> + <link rel="shortcut icon" href="#"> + <link rel="stylesheet" type="text/css" href="/static/jquery-ui.min.css"/> + <link rel="stylesheet" type="text/css" href="/static/jquery.dataTables.min.css"/> + <link rel="stylesheet" type="text/css" href="/static/highlight.github.min.css"/> + <style> + body { + font-family: "Trebuchet MS", sans-serif; + margin: 50px; + } + .chart { + height: 700px; max-width: 920px; margin: 0px auto; + } + </style> + </head> + + <body> + <h1 id="heading-title">Title</h1> + <div> + Available benchmarks: + <ul id="toc"></ul> + </div> + <div id="bm-results"></div> + <div><pre id="dbg"></pre></div> + <!-- scripts --> + <script type="text/javascript" src="/static/jquery-3.3.1.min.js"></script> + <script type="text/javascript" src="/static/jquery-ui.js"></script> + <script type="text/javascript" src="/static/jquery.canvasjs.min.js"></script> + <script type="text/javascript" src="/static/Chart.min.js"></script> + <script type="text/javascript" src="/static/jquery.dataTables.min.js"></script> + <script type="text/javascript" src="/static/highlight.min.js"></script> + <script type="text/javascript" src="/bm.js"></script> + <script type="text/javascript"> + $(document).ready(function() { + $.getJSON('/manifest.json', function(specs){ + loadSpecs(specs); + }) + }); + </script> + </body> +</html> diff --git a/thirdparty/ryml/ext/c4core/cmake/c4CatSources.cmake b/thirdparty/ryml/ext/c4core/cmake/c4CatSources.cmake new file mode 100644 index 000000000..1633989af --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4CatSources.cmake @@ -0,0 +1,105 @@ +if(NOT _c4CatSourcesIncluded) +set(_c4CatSourcesIncluded ON) + + +#------------------------------------------------------------------------------ +# concatenate the source files to an output file, adding preprocessor adjustment +# for correct file/line reporting +function(c4_cat_sources files output umbrella_target) + _c4_cat_sources_create_cat(cat) + c4_to_full_path("${files}" full_files) # we must work with full paths + c4_separate_list("${full_files}" sepfiles) # and use a string instead of a list + c4_dbg("${_c4_prefix}: catting sources to ${output}") + if(NOT EXISTS "${output}") + # the cat command is executed at build time, but we need the output + # file to exist to be able to create the target. so to bootstrap, just + # run the command now + c4_dbg("${_c4_prefix}: creating ${output} for the first time") + execute_process( + COMMAND ${cat} "${sepfiles}" "${output}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + ) + else() + c4_dbg("output exists: ${output}") + endif() + # add a custom command invoking our cat script for the input files + add_custom_command(OUTPUT ${output} + COMMAND ${cat} "${sepfiles}" "${output}" + DEPENDS ${files} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + COMMENT "concatenating sources to ${output}") + if(NOT TARGET ${umbrella_target}) + add_custom_target(${umbrella_target} DEPENDS ${output} ${files}) + endif() +endfunction(c4_cat_sources) + + +#------------------------------------------------------------------------------ +# get a cat script +function(_c4_cat_sources_create_cat catfile) + # create a script to concatenate the sources + if(WIN32) + set(cat ${CMAKE_BINARY_DIR}/_c4catfiles.bat) + set(cattmp ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/_c4catfiles.bat) + else() + set(cat ${CMAKE_BINARY_DIR}/_c4catfiles.sh) + set(cattmp ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/_c4catfiles.sh) + endif() + set(${catfile} ${cat} PARENT_SCOPE) + if(NOT EXISTS ${cat}) + if(WIN32) + file(WRITE ${cattmp} " +setlocal EnableDelayedExpansion +set \"src_files=%1\" +set \"out_file=%2\" +echo.>\"out_file%\" +for %%f in (%src_files%) do ( + echo.>>\"%out_file%\" + echo.>>\"%out_file%\" + echo \"/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/\".>>\"%out_file%\" + echo \"/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/\".>>\"%out_file%\" + echo \"/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/\".>>\"%out_file%\" + echo \"#line 1 \\\"%%f\\\" // reset __LINE__ and __FILE__ to the correct value\".>>\"%out_file%\" + type %%f>>\"%out_file%\" +) +") + else() + file(WRITE ${cattmp} "#!/bin/sh + +src_files=$1 +out_file=$2 +#echo \"src_files $src_files\" +#echo \"out_file $out_file\" + +cat > $out_file << EOF +// DO NOT EDIT. +// this is an auto-generated file, and will be overwritten +EOF +for f in $src_files ; do + cat >> $out_file <<EOF + + +/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ +/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ +/*BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB*/ +#line 1 \"$f\" +EOF + cat $f >> $out_file +done + +echo \"Wrote output to $out_file\" +") + endif() + # add execute permissions + get_filename_component(catdir ${cat} DIRECTORY) + file(COPY ${cattmp} DESTINATION ${catdir} + FILE_PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + ) + endif() +endfunction() + + +endif(NOT _c4CatSourcesIncluded) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4Doxygen.cmake b/thirdparty/ryml/ext/c4core/cmake/c4Doxygen.cmake new file mode 100644 index 000000000..b5e018872 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4Doxygen.cmake @@ -0,0 +1,121 @@ +# (C) 2019 Joao Paulo Magalhaes <[email protected]> +if(NOT _c4_doxygen_included) +set(_c4_doxygen_included ON) + + +#------------------------------------------------------------------------------ +# TODO use customizations from https://cmake.org/cmake/help/v3.9/module/FindDoxygen.html +function(c4_setup_doxygen umbrella_option) + cmake_dependent_option(${_c4_uprefix}BUILD_DOCS "Enable targets to build documentation for ${prefix}" ON "${umbrella_option}" OFF) + if(${_c4_uprefix}BUILD_DOCS) + find_package(Doxygen QUIET) + if(DOXYGEN_FOUND) + c4_log("enabling documentation targets") + else() + c4_dbg("doxygen not found") + endif() + endif() +endfunction() + +#------------------------------------------------------------------------------ +function(c4_add_doxygen doc_name) + if(NOT ${_c4_uprefix}BUILD_DOCS) + return() + endif() + # + set(opt0 + ) + set(opt1 + DOXYFILE DOXYFILE_IN + PROJ + PROJ_BRIEF + VERSION + OUTPUT_DIR + CLANG_DATABASE_PATH + ) + set(optN + INPUT + FILE_PATTERNS + EXCLUDE + EXCLUDE_PATTERNS + EXCLUDE_SYMBOLS + STRIP_FROM_PATH + STRIP_FROM_INC_PATH + EXAMPLE_PATH + ) + cmake_parse_arguments("" "${opt0}" "${opt1}" "${optN}" ${ARGN}) + # + if(NOT _PROJ) + set(_PROJ ${_c4_ucprefix}) + endif() + if(NOT _DOXYFILE AND NOT _DOXYFILE_IN) + set(_DOXYFILE_IN ${CMAKE_CURRENT_LIST_DIR}/Doxyfile.in) + endif() + if(NOT _OUTPUT_DIR) + if("${doc_name}" MATCHES "^[Dd]oc") + set(_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${doc_name}) + else() + set(_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doc/${doc_name}) + endif() + endif() + # + _c4_doxy_fwd_to_cmd(_PROJ OFF) + _c4_doxy_fwd_to_cmd(_PROJ_BRIEF OFF) + _c4_doxy_fwd_to_cmd(_VERSION OFF) + _c4_doxy_fwd_to_cmd(_OUTPUT_DIR OFF) + _c4_doxy_fwd_to_cmd(_CLANG_DATABASE_PATH OFF) + _c4_doxy_fwd_to_cmd(_INPUT ON) + _c4_doxy_fwd_to_cmd(_FILE_PATTERNS ON) + _c4_doxy_fwd_to_cmd(_EXCLUDE ON) + _c4_doxy_fwd_to_cmd(_EXCLUDE_PATTERNS ON) + _c4_doxy_fwd_to_cmd(_EXCLUDE_SYMBOLS ON) + _c4_doxy_fwd_to_cmd(_STRIP_FROM_PATH ON) + _c4_doxy_fwd_to_cmd(_STRIP_FROM_INC_PATH ON) + _c4_doxy_fwd_to_cmd(_EXAMPLE_PATH ON) + # + if("${doc_name}" MATCHES "^[Dd]oc") + set(tgt ${_c4_lcprefix}-${doc_name}) + else() + set(tgt ${_c4_lcprefix}-doc-${doc_name}) + endif() + # + if(_DOXYFILE) + set(doxyfile_out ${_DOXYFILE}) + elseif(_DOXYFILE_IN) + set(doxyfile_out ${_OUTPUT_DIR}/Doxyfile) + set(config_script ${_c4_project_dir}/c4DoxygenConfig.cmake) + add_custom_command(OUTPUT ${doxyfile_out} + COMMAND ${CMAKE_COMMAND} -E remove -f ${doxyfile_out} + COMMAND ${CMAKE_COMMAND} -DDOXYFILE_IN=${_DOXYFILE_IN} -DDOXYFILE_OUT=${doxyfile_out} ${defs} '-DALLVARS=${allvars}' '-DLISTVARS=${listvars}' -P ${config_script} + DEPENDS ${_DOXYFILE_IN} ${config_script} + COMMENT "${tgt}: generating ${doxyfile_out}" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + endif() + # + add_custom_target(${tgt} + COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile_out} + DEPENDS ${doxyfile_out} + WORKING_DIRECTORY ${_OUTPUT_DIR} + COMMENT "${tgt}: docs will be placed in ${_OUTPUT_DIR}" + VERBATIM) + _c4_set_target_folder(${tgt} doc) +endfunction() + + +macro(_c4_doxy_fwd_to_cmd varname is_list) + if(NOT ("${${varname}}" STREQUAL "")) + if("${defs}" STREQUAL "") + set(li "-D${varname}=${${varname}}") + else() + set(li ${defs}) + list(APPEND li "-D${varname}='${${varname}}'") + endif() + set(defs ${li}) + endif() + set(allvars "${allvars};${varname}") + if(${is_list}) + set(listvars "${listvars};${varname}") + endif() +endmacro() + +endif(NOT _c4_doxygen_included) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4DoxygenConfig.cmake b/thirdparty/ryml/ext/c4core/cmake/c4DoxygenConfig.cmake new file mode 100644 index 000000000..b472cab88 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4DoxygenConfig.cmake @@ -0,0 +1,24 @@ +function(_c4_doxy_list_to_str var) + set(il) + foreach(i ${${var}}) + if("${il}" STREQUAL "") + set(il "${i}") + else() + set(il "${il} ${i}") + endif() + endforeach() + set(${var} "${il}" PARENT_SCOPE) +endfunction() + +string(REPLACE " " ";" ALLVARS ${ALLVARS}) +string(REPLACE " " ";" LISTVARS ${LISTVARS}) + +foreach(var ${LISTVARS}) + _c4_doxy_list_to_str(${var}) +endforeach() + +foreach(var ${ALLVARS}) + message(STATUS "${var}='${${var}}'") +endforeach() + +configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4GetTargetPropertyRecursive.cmake b/thirdparty/ryml/ext/c4core/cmake/c4GetTargetPropertyRecursive.cmake new file mode 100644 index 000000000..45a74aa9e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4GetTargetPropertyRecursive.cmake @@ -0,0 +1,186 @@ +if(NOT _c4_GTPR_included) +set(_c4_GTPR_included ON) + +function(c4_get_target_property_recursive outputvar target property) + # + # helps for debugging + if(_stack) + set(_stack "${_stack}/${target}") + else() + set(_stack "${property}:${target}") + endif() + # + # what type of target is this? + get_target_property(_rec_target_type ${target} TYPE) + c4_dbg("${_stack} [type=${_rec_target_type}]: get property ${property}") + # + # adjust the property names for interface targets + set(_ept_prop_ll LINK_LIBRARIES) + if(_rec_target_type STREQUAL "INTERFACE_LIBRARY") + set(_ept_prop_ll INTERFACE_LINK_LIBRARIES) + if(property STREQUAL "INCLUDE_DIRECTORIES") + c4_dbg("${_stack} [type=${_rec_target_type}]: property ${property} ---> INTERFACE_INCLUDE_DIRECTORIES") + set(property INTERFACE_INCLUDE_DIRECTORIES) + elseif(property STREQUAL "LINK_LIBRARIES") + c4_dbg("${_stack} [type=${_rec_target_type}]: property ${property} ---> INTERFACE_LINK_LIBRARIES") + set(property INTERFACE_LINK_LIBRARIES) + endif() + endif() + # + get_target_property(_ept_li ${target} ${property}) + c4_dbg("${_stack} [type=${_rec_target_type}]: property ${property}=${_ept_li}") + if(NOT _ept_li) # the property may not be set (ie foo-NOTFOUND) + set(_ept_li) # so clear it in that case + endif() + # + # now descend and append the property for each of the linked libraries + get_target_property(_ept_deps ${target} ${_ept_prop_ll}) + if(_ept_deps) + foreach(_ept_ll ${_ept_deps}) + if(TARGET ${_ept_ll}) + c4_get_target_property_recursive(_ept_out ${_ept_ll} ${property}) + list(APPEND _ept_li ${_ept_out}) + endif() + endforeach() + endif() + # + foreach(le_ ${_ept_li}) + string(STRIP "${le_}" le) + if(NOT le) + elseif("${le}" STREQUAL "") + else() + list(APPEND _ept_li_f ${le}) + endif() + endforeach() + c4_dbg("${_stack} [type=${_rec_target_type}]: final=${_ept_li_f}") + set(${outputvar} ${_ept_li_f} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + + +function(c4_set_transitive_property target prop_name prop_value) + set_target_properties(${target} PROPERTIES "${prop_name}" "${prop_value}") +endfunction() + + +function(c4_append_transitive_property target prop_name prop_value) + get_target_property(curr_value ${target} "${prop_name}") + if(curr_value) + list(APPEND curr_value "${prop_value}") + else() + set(curr_value "${prop_value}") + endif() + c4_set_transitive_property(${target} "${prop_name}" "${curr_value}") +endfunction() + + +# TODO: maybe we can use c4_get_target_property_recursive()? +function(c4_get_transitive_property target prop_name out) + if(NOT TARGET ${target}) + return() + endif() + # these will be the names of the variables we'll use to cache the result + set(_trval _C4_TRANSITIVE_${prop_name}) + set(_trmark _C4_TRANSITIVE_${prop_name}_DONE) + # + get_target_property(cached ${target} ${_trmark}) # is it cached already + if(cached) + get_target_property(p ${target} _C4_TRANSITIVE_${prop_name}) + set(${out} ${p} PARENT_SCOPE) + #c4_dbg("${target}: c4_get_transitive_property ${target} ${prop_name}: cached='${p}'") + else() + #c4_dbg("${target}: gathering transitive property: ${prop_name}...") + set(interleaved) + get_target_property(lv ${target} ${prop_name}) + if(lv) + list(APPEND interleaved ${lv}) + endif() + c4_get_transitive_libraries(${target} LINK_LIBRARIES libs) + c4_get_transitive_libraries(${target} INTERFACE_LINK_LIBRARIES ilibs) + list(APPEND libs ${ilibs}) + foreach(lib ${libs}) + #c4_dbg("${target}: considering ${lib}...") + if(NOT lib) + #c4_dbg("${target}: considering ${lib}: not found, skipping...") + continue() + endif() + if(NOT TARGET ${lib}) + #c4_dbg("${target}: considering ${lib}: not a target, skipping...") + continue() + endif() + get_target_property(lv ${lib} ${prop_name}) + if(lv) + list(APPEND interleaved ${lv}) + endif() + c4_get_transitive_property(${lib} ${prop_name} v) + if(v) + list(APPEND interleaved ${v}) + endif() + #c4_dbg("${target}: considering ${lib}---${interleaved}") + endforeach() + #c4_dbg("${target}: gathering transitive property: ${prop_name}: ${interleaved}") + set(${out} ${interleaved} PARENT_SCOPE) + get_target_property(aliased_target ${target} ALIASED_TARGET) + if(NOT aliased_target) + set_target_properties(${target} PROPERTIES + ${_trmark} ON + ${_trval} "${interleaved}") + endif() + endif() +endfunction() + + +function(c4_get_transitive_libraries target prop_name out) + if(NOT TARGET ${target}) + return() + endif() + # these will be the names of the variables we'll use to cache the result + set(_trval _C4_TRANSITIVE_${prop_name}) + set(_trmark _C4_TRANSITIVE_${prop_name}_DONE) + # + get_target_property(cached ${target} ${_trmark}) + if(cached) + get_target_property(p ${target} ${_trval}) + set(${out} ${p} PARENT_SCOPE) + #c4_dbg("${target}: c4_get_transitive_libraries ${target} ${prop_name}: cached='${p}'") + else() + #c4_dbg("${target}: gathering transitive libraries: ${prop_name}...") + get_target_property(target_type ${target} TYPE) + set(interleaved) + if(NOT ("${target_type}" STREQUAL "INTERFACE_LIBRARY") + AND ("${prop_name}" STREQUAL LINK_LIBRARIES)) + get_target_property(l ${target} ${prop_name}) + foreach(ll ${l}) + #c4_dbg("${target}: considering ${ll}...") + if(NOT ll) + #c4_dbg("${target}: considering ${ll}: not found, skipping...") + continue() + endif() + if(NOT ll) + #c4_dbg("${target}: considering ${ll}: not a target, skipping...") + continue() + endif() + list(APPEND interleaved ${ll}) + c4_get_transitive_libraries(${ll} ${prop_name} v) + if(v) + list(APPEND interleaved ${v}) + endif() + #c4_dbg("${target}: considering ${ll}---${interleaved}") + endforeach() + endif() + #c4_dbg("${target}: gathering transitive libraries: ${prop_name}: result='${interleaved}'") + set(${out} ${interleaved} PARENT_SCOPE) + get_target_property(aliased_target ${target} ALIASED_TARGET) + if(NOT aliased_target) + set_target_properties(${target} PROPERTIES + ${_trmark} ON + ${_trval} "${interleaved}") + endif() + endif() +endfunction() + +endif(NOT _c4_GTPR_included) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake b/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake new file mode 100644 index 000000000..60c8717fe --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4Project.cmake @@ -0,0 +1,3691 @@ +if(NOT _c4_project_included) +set(_c4_project_included ON) +set(_c4_project_file ${CMAKE_CURRENT_LIST_FILE}) +set(_c4_project_dir ${CMAKE_CURRENT_LIST_DIR}) + + +# "I didn't have time to write a short letter, so I wrote a long one +# instead." -- Mark Twain +# +# ... Eg, hopefully this code will be cleaned up. There's a lot of +# code here that can be streamlined into a more intuitive arrangement. + + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +include(ConfigurationTypes) +include(CreateSourceGroup) +include(c4SanitizeTarget) +include(c4StaticAnalysis) +include(PrintVar) +include(c4CatSources) +include(c4Doxygen) +include(PatchUtils) + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +# define c4 project settings + +set(C4_EXTERN_DIR "$ENV{C4_EXTERN_DIR}" CACHE PATH "the directory where imported projects should be looked for (or cloned in when not found)") +set(C4_DBG_ENABLED OFF CACHE BOOL "enable detailed cmake logs in c4Project code") +set(C4_LIBRARY_TYPE "" CACHE STRING "default library type: either \"\"(defer to BUILD_SHARED_LIBS),INTERFACE,STATIC,SHARED,MODULE") +set(C4_SOURCE_TRANSFORM NONE CACHE STRING "global source transform method") +set(C4_HDR_EXTS "h;hpp;hh;h++;hxx" CACHE STRING "list of header extensions for determining which files are headers") +set(C4_SRC_EXTS "c;cpp;cc;c++;cxx;cu;" CACHE STRING "list of compilation unit extensions for determining which files are sources") +set(C4_ADD_EXTS "natvis" CACHE STRING "list of additional file extensions that might be added as sources to targets") +set(C4_GEN_SRC_EXT "cpp" CACHE STRING "the extension of the output source files resulting from concatenation") +set(C4_GEN_HDR_EXT "hpp" CACHE STRING "the extension of the output header files resulting from concatenation") +set(C4_CXX_STANDARDS "20;17;14;11" CACHE STRING "list of CXX standards") +set(C4_CXX_STANDARD_DEFAULT "11" CACHE STRING "the default CXX standard for projects not specifying one") + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +macro(c4_log) + message(STATUS "${_c4_prefix}: ${ARGN}") +endmacro() + + +macro(c4_err) + message(FATAL_ERROR "${_c4_prefix}: ${ARGN}") +endmacro() + + +macro(c4_dbg) + if(C4_DBG_ENABLED) + message(STATUS "${_c4_prefix}: ${ARGN}") + endif() +endmacro() + + +macro(c4_log_var varname) + c4_log("${varname}=${${varname}} ${ARGN}") +endmacro() +macro(c4_log_vars) + set(____s____) + foreach(varname ${ARGN}) + set(____s____ "${____s____}${varname}=${${varname}} ") + endforeach() + c4_log("${____s____}") +endmacro() +macro(c4_dbg_var varname) + c4_dbg("${varname}=${${varname}} ${ARGN}") +endmacro() +macro(c4_log_var_if varname) + if(${varname}) + c4_log("${varname}=${${varname}} ${ARGN}") + endif() +endmacro() +macro(c4_dbg_var_if varname) + if(${varname}) + c4_dbg("${varname}=${${varname}} ${ARGN}") + endif() +endmacro() + + +macro(_c4_show_pfx_vars) + if(NOT ("${ARGN}" STREQUAL "")) + c4_log("prefix vars: ${ARGN}") + endif() + print_var(_c4_prefix) + print_var(_c4_ocprefix) + print_var(_c4_ucprefix) + print_var(_c4_lcprefix) + print_var(_c4_oprefix) + print_var(_c4_uprefix) + print_var(_c4_lprefix) +endmacro() + + +function(c4_zero_pad padded size str) + string(LENGTH "${str}" len) + math(EXPR numchars "${size} - ${len}") + if(numchars EQUAL 0) + set(${padded} "${str}" PARENT_SCOPE) + else() + set(out "${str}") + math(EXPR ncm1 "${numchars} - 1") + foreach(z RANGE ${ncm1}) + set(out "0${out}") + endforeach() + set(${padded} "${out}" PARENT_SCOPE) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# handy macro for dealing with arguments in one single statement. +# look for example usage cases below. +macro(_c4_handle_args) + set(opt0arg + ) + set(opt1arg + _PREFIX + ) + set(optNarg + _ARGS0 + _ARGS1 + _ARGSN + _ARGS + _DEPRECATE + ) + # parse the arguments to this macro to find out the required arguments + cmake_parse_arguments("__c4ha" "${opt0arg}" "${opt1arg}" "${optNarg}" ${ARGN}) + # now parse the required arguments + cmake_parse_arguments("${__c4ha__PREFIX}" "${__c4ha__ARGS0}" "${__c4ha__ARGS1}" "${__c4ha__ARGSN}" ${__c4ha__ARGS}) + # raise an error on deprecated arguments + foreach(a ${__c4ha__DEPRECATE}) + list(FIND __c4ha__ARGS ${a} contains) + if(NOT (${contains} EQUAL -1)) + c4err("${a} is deprecated") + endif() + endforeach() +endmacro() + +# fallback to provided default(s) if argument is not set +macro(_c4_handle_arg argname) + if("${_${argname}}" STREQUAL "") + set(_${argname} "${ARGN}") + else() + set(_${argname} "${_${argname}}") + endif() +endmacro() +macro(_c4_handle_arg_no_pfx argname) + if("${${argname}}" STREQUAL "") + set(${argname} "${ARGN}") + else() + set(${argname} "${${argname}}") + endif() +endmacro() + + +# if ${_${argname}} is non empty, return it +# otherwise, fallback to ${_c4_uprefix}${argname} +# otherwise, fallback to C4_${argname} +# otherwise, fallback to provided default through ${ARGN} +macro(_c4_handle_arg_or_fallback argname) + if(NOT ("${_${argname}}" STREQUAL "")) + c4_dbg("handle arg ${argname}: picking explicit value _${argname}=${_${argname}}") + else() + foreach(_c4haf_varname "${_c4_uprefix}${argname}" "C4_${argname}" "${argname}" "CMAKE_${argname}") + set(v ${${_c4haf_varname}}) + if("${v}" STREQUAL "") + c4_dbg("handle arg ${argname}: ${_c4haf_varname}: empty, continuing") + else() + c4_dbg("handle arg ${argname}: ${_c4haf_varname}=${v} not empty!") + c4_setg(_${argname} "${v}") + break() + endif() + endforeach() + if("${_${argname}}" STREQUAL "") + c4_dbg("handle arg ${argname}: picking default: ${ARGN}") + c4_setg(_${argname} "${ARGN}") + endif() + endif() +endmacro() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_get_config var name) + c4_dbg("get_config: ${var} ${name}") + c4_get_from_first_of(config ${ARGN} VARS ${_c4_uprefix}${name} C4_${name} ${name}) + c4_dbg("get_config: ${var} ${name}=${config}") + set(${var} ${config} PARENT_SCOPE) +endfunction() + + +function(c4_get_from_first_of var) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + REQUIRED # raise an error if no set variable was found + ENV # if none of the provided vars is given, + # then search next on environment variables + # of the same name, using the same sequence + _ARGS1 + DEFAULT + _ARGSN + VARS + ) + c4_dbg("get_from_first_of(): searching ${var}") + foreach(_var ${_VARS}) + set(val ${${_var}}) + c4_dbg("${var}: searching ${_var}=${val}") + if(NOT ("${val}" STREQUAL "")) + set(${var} "${val}" PARENT_SCOPE) + return() + endif() + endforeach() + if(_ENV) + foreach(_envvar ${_VARS}) + set(val $ENV{${_envvar}}) + c4_dbg("${var}: searching environment variable ${_envvar}=${val}") + if(NOT ("${val}" STREQUAL "")) + c4_dbg("${var}: picking ${val} from ${_envvar}") + set(${var} "${val}" PARENT_SCOPE) + return() + endif() + endforeach() + endif() + if(_REQUIRED) + c4_err("could not find a value for the variable ${var}") + endif() + set(${var} ${_DEFAULT} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# assumes a prior call to project() +function(c4_project) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + STANDALONE # Declare that targets from this project MAY be + # compiled in standalone mode. In this mode, any + # designated libraries on which a target depends + # will be incorporated into the target instead of + # being linked with it. The effect is to "flatten" + # those libraries into the requesting library, with + # their sources now becoming part of the requesting + # library; their dependencies are transitively handled. + # Note that requesting targets must explicitly + # opt-in to this behavior via the INCORPORATE + # argument to c4_add_library() or + # c4_add_executable(). Note also that this behavior + # is only enabled if this project's option + # ${prefix}_STANDALONE or C4_STANDALONE is set to ON. + _ARGS1 # one-value macro arguments + AUTHOR # specify author(s); used in cpack + VERSION # cmake does not accept semantic versioning so we provide + # that here (see https://gitlab.kitware.com/cmake/cmake/-/issues/16716) + CXX_STANDARD # one of latest;${C4_VALID_CXX_STANDARDS} + # if this is not provided, falls back on + # ${uprefix}CXX_STANDARD, then C4_CXX_STANDARD, + # then CXX_STANDARD. if none are provided, + # defaults to 11 + _ARGSN # multi-value macro arguments + ) + # get the prefix from the call to project() + set(prefix ${PROJECT_NAME}) + string(TOUPPER "${prefix}" ucprefix) # ucprefix := upper case prefix + string(TOLOWER "${prefix}" lcprefix) # lcprefix := lower case prefix + if(NOT _c4_prefix) + c4_setg(_c4_is_root_proj ON) + c4_setg(_c4_root_proj ${prefix}) + c4_setg(_c4_root_uproj ${ucprefix}) + c4_setg(_c4_root_lproj ${lcprefix}) + c4_setg(_c4_curr_path "") + else() + c4_setg(_c4_is_root_proj OFF) + if(_c4_curr_path) + c4_setg(_c4_curr_path "${_c4_curr_path}/${prefix}") + else() + c4_setg(_c4_curr_path "${prefix}") + endif() + endif() + c4_setg(_c4_curr_subproject ${prefix}) + # get the several prefix flavors + c4_setg(_c4_ucprefix ${ucprefix}) + c4_setg(_c4_lcprefix ${lcprefix}) + c4_setg(_c4_ocprefix ${prefix}) # ocprefix := original case prefix + c4_setg(_c4_prefix ${prefix}) # prefix := original prefix + c4_setg(_c4_oprefix ${prefix}) # oprefix := original prefix + c4_setg(_c4_uprefix ${_c4_ucprefix}) # upper prefix: for variables + c4_setg(_c4_lprefix ${_c4_lcprefix}) # lower prefix: for targets + if(_c4_oprefix) + c4_setg(_c4_oprefix "${_c4_oprefix}_") + endif() + if(_c4_uprefix) + c4_setg(_c4_uprefix "${_c4_uprefix}_") + endif() + if(_c4_lprefix) + c4_setg(_c4_lprefix "${_c4_lprefix}-") + endif() + # + if(_STANDALONE) + option(${_c4_uprefix}STANDALONE + "Enable compilation of opting-in targets from ${_c4_lcprefix} in standalone mode (ie, incorporate subprojects as specified in the INCORPORATE clause to c4_add_library/c4_add_target)" + ${_c4_is_root_proj}) + c4_setg(_c4_root_proj_standalone ${_c4_uprefix}STANDALONE) + endif() + _c4_handle_arg_or_fallback(CXX_STANDARD ${C4_CXX_STANDARD_DEFAULT}) + _c4_handle_arg(VERSION 0.0.0-pre0) + _c4_handle_arg(AUTHOR "") + _c4_handle_semantic_version(${_VERSION}) + # + # make sure project-wide settings are defined -- see cmake's + # documentation for project(), which defines these and other + # variables + if("${PROJECT_DESCRIPTION}" STREQUAL "") + c4_setg(PROJECT_DESCRIPTION "${prefix}") + c4_setg(${prefix}_DESCRIPTION "${prefix}") + endif() + if("${PROJECT_HOMEPAGE_URL}" STREQUAL "") + c4_setg(PROJECT_HOMEPAGE_URL "") + c4_setg(${prefix}_HOMEPAGE_URL "") + endif() + # other specific c4_project properties + c4_setg(PROJECT_AUTHOR "${_AUTHOR}") + c4_setg(${prefix}_AUTHOR "${_AUTHOR}") + + # CXX standard + if("${_CXX_STANDARD}" STREQUAL "latest") + _c4_find_latest_supported_cxx_standard(_CXX_STANDARD) + endif() + c4_log("using C++ standard: C++${_CXX_STANDARD}") + c4_set_proj_prop(CXX_STANDARD "${_CXX_STANDARD}") + c4_setg(${_c4_uprefix}CXX_STANDARD "${_CXX_STANDARD}") + if(${_CXX_STANDARD}) + c4_set_cxx(${_CXX_STANDARD}) + endif() + + # we are opinionated with respect to directory structure + c4_setg(${_c4_uprefix}SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src) + c4_setg(${_c4_uprefix}EXT_DIR ${CMAKE_CURRENT_LIST_DIR}/ext) + c4_setg(${_c4_uprefix}API_DIR ${CMAKE_CURRENT_LIST_DIR}/api) + # opionionated also for directory test + # opionionated also for directory bm (benchmarks) + + if("${C4_DEV}" STREQUAL "") + option(C4_DEV "enable development targets for all c4 projects" OFF) + endif() + option(${_c4_uprefix}DEV "enable development targets: tests, benchmarks, sanitize, static analysis, coverage" ${C4_DEV}) + + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/test") + cmake_dependent_option(${_c4_uprefix}BUILD_TESTS "build unit tests" ON ${_c4_uprefix}DEV OFF) + else() + c4_dbg("no tests: directory does not exist: ${CMAKE_CURRENT_LIST_DIR}/test") + endif() + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/bm") + cmake_dependent_option(${_c4_uprefix}BUILD_BENCHMARKS "build benchmarks" ON ${_c4_uprefix}DEV OFF) + else() + c4_dbg("no benchmarks: directory does not exist: ${CMAKE_CURRENT_LIST_DIR}/bm") + endif() + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/api") + cmake_dependent_option(${_c4_uprefix}BUILD_API "build API" OFF ${_c4_uprefix}DEV OFF) + else() + c4_dbg("no API generation: directory does not exist: ${CMAKE_CURRENT_LIST_DIR}/api") + endif() + if(_c4_is_root_proj) + c4_setup_coverage() + endif() + c4_setup_valgrind(${_c4_uprefix}DEV) + c4_setup_sanitize(${_c4_uprefix}DEV) + c4_setup_static_analysis(${_c4_uprefix}DEV) + c4_setup_doxygen(${_c4_uprefix}DEV) + + # option to use libc++ + option(${_c4_uprefix}USE_LIBCXX "use libc++ instead of the default standard library" OFF) + if(${_c4_uprefix}USE_LIBCXX) + if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + c4_log("using libc++") + list(APPEND CMAKE_CXX_FLAGS -stdlib=libc++) + list(APPEND CMAKE_EXE_LINKER_FLAGS -lc++) + list(APPEND CMAKE_MODULE_LINKER_FLAGS -lc++) + list(APPEND CMAKE_SHARED_LINKER_FLAGS -lc++) + list(APPEND CMAKE_STATIC_LINKER_FLAGS -lc++) + else() + c4_err("libc++ can only be used with clang") + endif() + endif() + + # default compilation flags + set(${_c4_uprefix}CXX_FLAGS "${${_c4_uprefix}CXX_FLAGS_FWD}" CACHE STRING "compilation flags for ${_c4_prefix} targets") + set(${_c4_uprefix}CXX_LINKER_FLAGS "${${_c4_uprefix}CXX_LINKER_FLAGS_FWD}" CACHE STRING "linker flags for ${_c4_prefix} targets") + c4_dbg_var_if(${_c4_uprefix}CXX_LINKER_FLAGS_FWD) + c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS_FWD) + c4_dbg_var_if(${_c4_uprefix}CXX_LINKER_FLAGS) + c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS) + + # Dev compilation flags, appended to the project's flags. They + # are enabled when in dev mode, but provided as a (default-disabled) + # option when not in dev mode + c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS_OPT_FWD) + c4_setg(${_c4_uprefix}CXX_FLAGS_OPT "${${_c4_uprefix}CXX_FLAGS_OPT_FWD}") + c4_optional_compile_flags_dev(WERROR "Compile with warnings as errors" + GCC_CLANG -Werror -pedantic-errors + MSVC /WX + ) + c4_optional_compile_flags_dev(STRICT_ALIASING "Enable strict aliasing" + GCC_CLANG -fstrict-aliasing + MSVC # does it have this? + ) + c4_optional_compile_flags_dev(PEDANTIC "Compile in pedantic mode" + GCC ${_C4_PEDANTIC_FLAGS_GCC} + CLANG ${_C4_PEDANTIC_FLAGS_CLANG} + MSVC ${_C4_PEDANTIC_FLAGS_MSVC} + ) + c4_dbg_var_if(${_c4_uprefix}CXX_FLAGS_OPT) +endfunction(c4_project) + + +# cmake: VERSION argument in project() does not accept semantic versioning +# see: https://gitlab.kitware.com/cmake/cmake/-/issues/16716 +macro(_c4_handle_semantic_version version) + # https://stackoverflow.com/questions/18658233/split-string-to-3-variables-in-cmake + string(REPLACE "." ";" version_list ${version}) + list(GET version_list 0 _major) + list(GET version_list 1 _minor) + list(GET version_list 2 _patch) + if("${_patch}" STREQUAL "") + set(_patch 1) + set(_tweak) + else() + string(REGEX REPLACE "([0-9]+)[-_.]?(.*)" "\\2" _tweak ${_patch}) # do this first + string(REGEX REPLACE "([0-9]+)[-_.]?(.*)" "\\1" _patch ${_patch}) # ... because this replaces _patch + endif() + # because cmake handles only numeric tweak fields, make sure to skip our + # semantic tweak field if it is not numeric + if(${_tweak} MATCHES "^[0-9]+$") + set(_safe_tweak ${_tweak}) + set(_safe_version ${_major}.${_minor}.${_patch}.${_tweak}) + else() + set(_safe_tweak) + set(_safe_version ${_major}.${_minor}.${_patch}) + endif() + c4_setg(PROJECT_VERSION_FULL ${version}) + c4_setg(PROJECT_VERSION ${_safe_version}) + c4_setg(PROJECT_VERSION_MAJOR ${_major}) + c4_setg(PROJECT_VERSION_MINOR ${_minor}) + c4_setg(PROJECT_VERSION_PATCH ${_patch}) + c4_setg(PROJECT_VERSION_TWEAK "${_safe_tweak}") + c4_setg(PROJECT_VERSION_TWEAK_FULL "${_tweak}") + c4_setg(${prefix}_VERSION_FULL ${version}) + c4_setg(${prefix}_VERSION ${_safe_version}) + c4_setg(${prefix}_VERSION_MAJOR ${_major}) + c4_setg(${prefix}_VERSION_MINOR ${_minor}) + c4_setg(${prefix}_VERSION_PATCH ${_patch}) + c4_setg(${prefix}_VERSION_TWEAK "${_safe_tweak}") + c4_setg(${prefix}_VERSION_TWEAK_FULL "${_tweak}") +endmacro() + + +# Add targets for testing (dir=./test), benchmark (dir=./bm) and API (dir=./api). +# Call this macro towards the end of the project's main CMakeLists.txt. +# Experimental feature: docs. +function(c4_add_dev_targets) + if(NOT CMAKE_CURRENT_LIST_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + c4_err("this macro needs to be called on the project's main CMakeLists.txt file") + endif() + # + if(${_c4_uprefix}BUILD_TESTS) + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/test") + c4_dbg("adding tests: ${CMAKE_CURRENT_LIST_DIR}/test") + enable_testing() # this must be done here (and not inside the + # test dir) so that the cmake-generated test + # targets are available at the top level + add_subdirectory(test) + endif() + endif() + # + if(${_c4_uprefix}BUILD_BENCHMARKS) + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/bm") + c4_dbg("adding benchmarks: ${CMAKE_CURRENT_LIST_DIR}/bm") + add_subdirectory(bm) + endif() + endif() + # + if(${_c4_uprefix}BUILD_API) + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/api") + c4_dbg("adding API: ${d}") + add_subdirectory(api) + endif() + endif() + # + # FIXME + c4_add_doxygen(doc DOXYFILE_IN ${_c4_project_dir}/Doxyfile.in + PROJ c4core + INPUT ${${_c4_uprefix}SRC_DIR} + EXCLUDE ${${_c4_uprefix}EXT_DIR} ${${_c4_uprefix}SRC_DIR}/c4/ext + STRIP_FROM_PATH ${${_c4_uprefix}SRC_DIR} + STRIP_FROM_INC_PATH ${${_c4_uprefix}SRC_DIR} + CLANG_DATABASE_PATH ${CMAKE_BINARY_DIR} + ) + c4_add_doxygen(doc-full DOXYFILE_IN ${_c4_project_dir}/Doxyfile.full.in + PROJ c4core + INPUT ${${_c4_uprefix}SRC_DIR} + EXCLUDE ${${_c4_uprefix}EXT_DIR} ${${_c4_uprefix}SRC_DIR}/c4/ext + STRIP_FROM_PATH ${${_c4_uprefix}SRC_DIR} + STRIP_FROM_INC_PATH ${${_c4_uprefix}SRC_DIR} + CLANG_DATABASE_PATH ${CMAKE_BINARY_DIR} + ) +endfunction() + + +function(_c4_get_san_targets target result) + _c4_get_tgt_prop(san_targets ${target} C4_SAN_TARGETS) + if(NOT san_targets) + #c4_err("${target} must have at least itself in its sanitized target list") + set(${result} ${target} PARENT_SCOPE) + else() + set(${result} ${san_targets} PARENT_SCOPE) + endif() +endfunction() + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- + +# utilities for compilation flags and defines + +# flags enabled only on dev mode +macro(c4_optional_compile_flags_dev tag desc) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + _ARGS1 + _ARGSN + MSVC # flags for Visual Studio compilers + GCC # flags for gcc compilers + CLANG # flags for clang compilers + GCC_CLANG # flags common to gcc and clang + _DEPRECATE + ) + cmake_dependent_option(${_c4_uprefix}${tag} "${desc}" ON ${_c4_uprefix}DEV OFF) + set(optname ${_c4_uprefix}${tag}) + if(${optname}) + c4_dbg("${optname} is enabled. Adding flags...") + if(MSVC) + set(flags ${_MSVC}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(flags ${_GCC_CLANG};${_CLANG}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(flags ${_GCC_CLANG};${_GCC}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set(flags ${_ALL};${_GCC_CLANG};${_GCC}) # FIXME + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") + set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) # FIXME + else() + c4_err("unknown compiler") + endif() + else() + c4_dbg("${optname} is disabled.") + endif() + if(flags) + c4_log("${tag} flags [${desc}]: ${flags}") + c4_setg(${_c4_uprefix}CXX_FLAGS_OPT "${${_c4_uprefix}CXX_FLAGS_OPT};${flags}") + endif() +endmacro() + + +function(c4_target_compile_flags target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + PUBLIC + PRIVATE + INTERFACE + AFTER # this is the default + BEFORE + _ARGS1 + _ARGSN + ALL # flags for all compilers + MSVC # flags for Visual Studio compilers + GCC # flags for gcc compilers + CLANG # flags for clang compilers + GCC_CLANG # flags common to gcc and clang + _DEPRECATE + ) + if(MSVC) + set(flags ${_ALL};${_MSVC}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(flags ${_ALL};${_GCC_CLANG};${_GCC}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set(flags ${_ALL};${_GCC_CLANG};${_GCC}) # FIXME + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") + set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) # FIXME + else() + c4_err("unknown compiler") + endif() + if(NOT flags) + c4_dbg("no compile flags to be set") + return() + endif() + if(_AFTER OR (NOT _BEFORE)) + set(mode) + c4_log("${target}: adding compile flags AFTER: ${flags}") + elseif(_BEFORE) + set(mode BEFORE) + c4_log("${target}: adding compile flags BEFORE: ${flags}") + endif() + _c4_get_san_targets(${target} san_targets) + foreach(st ${san_targets}) + if(_PUBLIC) + target_compile_options(${st} ${mode} PUBLIC ${flags}) + elseif(_PRIVATE) + target_compile_options(${st} ${mode} PRIVATE ${flags}) + elseif(_INTERFACE) + target_compile_options(${st} ${mode} INTERFACE ${flags}) + else() + c4_err("${target}: must have one of PUBLIC, PRIVATE or INTERFACE") + endif() + endforeach() +endfunction() + + +function(c4_target_definitions target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + PUBLIC + PRIVATE + INTERFACE + _ARGS1 + _ARGSN + ALL # defines for all compilers + MSVC # defines for Visual Studio compilers + GCC # defines for gcc compilers + CLANG # defines for clang compilers + GCC_CLANG # defines common to gcc and clang + _DEPRECATE + ) + if(MSVC) + set(flags ${_ALL};${_MSVC}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(flags ${_ALL};${_GCC_CLANG};${_CLANG}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(flags ${_ALL};${_GCC_CLANG};${_GCC}) + else() + c4_err("unknown compiler") + endif() + if(NOT flags) + c4_dbg("no compile flags to be set") + return() + endif() + if(_AFTER OR (NOT _BEFORE)) + set(mode) + c4_log("${target}: adding definitions AFTER: ${flags}") + elseif(_BEFORE) + set(mode BEFORE) + c4_log("${target}: adding definitions BEFORE: ${flags}") + endif() + _c4_get_san_targets(${target} san_targets) + foreach(st ${san_targets}) + if(_PUBLIC) + target_compile_definitions(${st} ${mode} PUBLIC ${flags}) + elseif(_PRIVATE) + target_compile_definitions(${st} ${mode} PRIVATE ${flags}) + elseif(_INTERFACE) + target_compile_definitions(${st} ${mode} INTERFACE ${flags}) + else() + c4_err("${target}: must have one of PUBLIC, PRIVATE or INTERFACE") + endif() + endforeach() +endfunction() + + +function(c4_target_remove_compile_flags target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + PUBLIC # remove only from public compile options + INTERFACE # remove only from interface compile options + _ARGS1 + _ARGSN + MSVC # flags for Visual Studio compilers + GCC # flags for gcc compilers + CLANG # flags for clang compilers + GCC_CLANG # flags common to gcc and clang + _DEPRECATE + ) + if(MSVC) + set(flags ${_MSVC}) + elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + set(flags ${_GCC_CLANG};${_CLANG}) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(flags ${_GCC_CLANG};${_GCC}) + else() + c4_err("unknown compiler") + endif() + if(NOT flags) + return() + endif() + _c4_get_san_targets(${target} san_targets) + foreach(st ${san_targets}) + if(_PUBLIC OR (NOT _INTERFACE)) + get_target_property(co ${st} COMPILE_OPTIONS) + if(co) + _c4_remove_entries_from_list("${flags}" co) + set_target_properties(${st} PROPERTIES COMPILE_OPTIONS "${co}") + endif() + endif() + if(_INTERFACE OR (NOT _PUBLIC)) + get_target_property(ico ${st} INTERFACE_COMPILE_OPTIONS) + if(ico) + _c4_remove_entries_from_list("${flags}" ico) + set_target_properties(${st} PROPERTIES INTERFACE_COMPILE_OPTIONS "${ico}") + endif() + endif() + endforeach() +endfunction() + + +function(_c4_remove_entries_from_list entries_to_remove list) + set(str ${${list}}) + string(REPLACE ";" "==?==" str "${str}") + foreach(entry ${entries_to_remove}) + string(REPLACE "${entry}" "" str "${str}") + endforeach() + string(REPLACE "==?==" ";" str "${str}") + string(REPLACE ";;" ";" str "${str}") + set(${list} "${str}" PARENT_SCOPE) +endfunction() + + + +# pedantic flags... +# default pedantic flags taken from: +# https://github.com/lefticus/cpp_starter_project/blob/master/cmake/CompilerWarnings.cmake +set(_C4_PEDANTIC_FLAGS_MSVC + /W4 # Baseline reasonable warnings + /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data + /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data + /w14263 # 'function': member function does not override any base class virtual member function + /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not + # be destructed correctly + /w14287 # 'operator': unsigned/negative constant mismatch + /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside + # the for-loop scope + /w14296 # 'operator': expression is always 'boolean_value' + /w14311 # 'variable': pointer truncation from 'type1' to 'type2' + /w14545 # expression before comma evaluates to a function which is missing an argument list + /w14546 # function call before comma missing argument list + /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect + /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? + /w14555 # expression has no effect; expected expression with side- effect + /w14619 # pragma warning: there is no warning number 'number' + /w14640 # Enable warning on thread un-safe static member initialization + /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. + /w14905 # wide string literal cast to 'LPSTR' + /w14906 # string literal cast to 'LPWSTR' + /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied + $<$<VERSION_GREATER:${MSVC_VERSION},1900>:/permissive-> # standards conformance mode for MSVC compiler (only vs2017+) + ) + +set(_C4_PEDANTIC_FLAGS_CLANG + -Wall + -Wextra + -pedantic + -Wshadow # warn the user if a variable declaration shadows one from a parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps + # catch hard to track down memory errors + #-Wold-style-cast # warn for c-style casts + -Wcast-align # warn for potential performance problem casts + -Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual function + -Wpedantic # warn if non-standard C++ is used + -Wconversion # warn on type conversions that may lose data + -Wsign-conversion # warn on sign conversions + -Wdouble-promotion # warn if float is implicit promoted to double + -Wfloat-equal # warn if comparing floats + -Wformat=2 # warn on security issues around functions that format output (ie printf) + ) + +set(_C4_PEDANTIC_FLAGS_GCC ${_C4_PEDANTIC_FLAGS_CLANG} + -Wlogical-op # where logical operations are used where bitwise were probably wanted + -Wuseless-cast # where you perform a cast to the same type + ) + +if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0)) + list(APPEND _C4_PEDANTIC_FLAGS_GCC + -Wnull-dereference # warn if a null dereference is detected + -Wmisleading-indentation # where indentation implies blocks where blocks do not exist + -Wduplicated-cond # where if-else chain has duplicated conditions + ) +endif() + +if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0)) + list(APPEND _C4_PEDANTIC_FLAGS_GCC + -Wduplicated-branches # where if-else branches have duplicated code + ) +endif() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_pack_project) + # if this is the top-level project... pack it. + if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + c4_log("packing the project: ${ARGN}") + c4_set_default_pack_properties(${ARGN}) + include(CPack) + endif() +endfunction() + + +# [WIP] set convenient defaults for the properties used by CPack +function(c4_set_default_pack_properties) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + _ARGS1 # one-value macro arguments + TYPE # one of LIBRARY, EXECUTABLE + _ARGSN # multi-value macro arguments + ) + set(pd "${PROJECT_SOURCE_DIR}") + _c4_handle_arg(TYPE EXECUTABLE) # default to EXECUTABLE + # + _c4_get_platform_tag(platform_tag) + if("${_TYPE}" STREQUAL "LIBRARY") + if(BUILD_SHARED_LIBS) + set(build_tag "-shared") + else() + set(build_tag "-static") + endif() + get_property(multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(multi_config) + # doesn't work because generators are not evaluated: set(build_tag "${build_tag}-$<CONFIG>") + # doesn't work because generators are not evaluated: set(build_tag "${build_tag}$<$<CONFIG:Debug>:-Debug>$<$<CONFIG:MinSizeRel>:-MinSizeRel>$<$<CONFIG:Release>:-Release>$<$<CONFIG:RelWithDebInfo>:-RelWithDebInfo>") + # see also https://stackoverflow.com/questions/44153730/how-to-change-cpack-package-file-name-based-on-configuration + if(CMAKE_BUILD_TYPE) # in the off-chance it was explicitly set + set(build_tag "${build_tag}-${CMAKE_BUILD_TYPE}") + endif() + else() + set(build_tag "${build_tag}-${CMAKE_BUILD_TYPE}") + endif() + elseif("${_TYPE}" STREQUAL "EXECUTABLE") + set(build_tag) + elseif() + c4_err("unknown TYPE: ${_TYPE}") + endif() + # + c4_setg(CPACK_VERBATIM_VARIABLES true) + c4_setg(CPACK_PACKAGE_VENDOR "${${_c4_prefix}_HOMEPAGE_URL}") + c4_setg(CPACK_PACKAGE_CONTACT "${${_c4_prefix}_AUTHOR}") + c4_setg(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${${_c4_prefix}_DESCRIPTION}") + if(EXISTS "${pd}/README.md") + c4_setg(CPACK_PACKAGE_DESCRIPTION_FILE "${pd}/README.md") + c4_setg(CPACK_PACKAGE_DESCRIPTION_README "${pd}/README.md") + c4_setg(CPACK_PACKAGE_DESCRIPTION_WELCOME "${pd}/README.md") + elseif(EXISTS "${pd}/README.txt") + c4_setg(CPACK_PACKAGE_DESCRIPTION_FILE "${pd}/README.txt") + c4_setg(CPACK_PACKAGE_DESCRIPTION_README "${pd}/README.txt") + c4_setg(CPACK_PACKAGE_DESCRIPTION_WELCOME "${pd}/README.txt") + endif() + if(EXISTS "${pd}/LICENSE.md") + c4_setg(CPACK_RESOURCE_FILE_LICENSE "${pd}/LICENSE.md") + elseif(EXISTS "${pd}/LICENSE.txt") + c4_setg(CPACK_RESOURCE_FILE_LICENSE "${pd}/LICENSE.txt") + endif() + c4_proj_get_version("${pd}" version_tag full major minor patch tweak) + c4_setg(CPACK_PACKAGE_VERSION "${full}") + c4_setg(CPACK_PACKAGE_VERSION_MAJOR "${major}") + c4_setg(CPACK_PACKAGE_VERSION_MINOR "${minor}") + c4_setg(CPACK_PACKAGE_VERSION_PATCH "${patch}") + c4_setg(CPACK_PACKAGE_VERSION_TWEAK "${tweak}") + c4_setg(CPACK_PACKAGE_INSTALL_DIRECTORY "${_c4_prefix}-${version_tag}") + c4_setg(CPACK_PACKAGE_FILE_NAME "${_c4_prefix}-${version_tag}-${platform_tag}${build_tag}") + if(WIN32 AND NOT UNIX) + # There is a bug in NSI that does not handle full UNIX paths properly. + # Make sure there is at least one set of four backlashes. + #c4_setg(CPACK_PACKAGE_ICON "${CMake_SOURCE_DIR}/Utilities/Release\\\\InstallIcon.bmp") + #c4_setg(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\MyExecutable.exe") + c4_setg(CPACK_NSIS_DISPLAY_NAME "${_c4_prefix} ${version_tag}") + c4_setg(CPACK_NSIS_HELP_LINK "${${_c4_prefix}_HOMEPAGE_URL}") + c4_setg(CPACK_NSIS_URL_INFO_ABOUT "${${_c4_prefix}_HOMEPAGE_URL}") + c4_setg(CPACK_NSIS_CONTACT "${${_c4_prefix}_AUTHOR}") + c4_setg(CPACK_NSIS_MODIFY_PATH ON) + else() + #c4_setg(CPACK_STRIP_FILES "bin/MyExecutable") + #c4_setg(CPACK_SOURCE_STRIP_FILES "") + c4_setg(CPACK_DEBIAN_PACKAGE_MAINTAINER "${${_c4_prefix}_AUTHOR}") + endif() + #c4_setg(CPACK_PACKAGE_EXECUTABLES "MyExecutable" "My Executable") +endfunction() + + +function(_c4_get_platform_tag tag_) + if(WIN32 AND NOT UNIX) + set(tag win) + elseif(APPLE) + set(tag apple) + elseif(UNIX) + set(tag unix) + else() + set(tag ${CMAKE_SYSTEM_NAME}) + endif() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) # 64 bits + set(tag ${tag}64) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) # 32 bits + set(tag ${tag}32) + else() + c4_err("not implemented") + endif() + set(${tag_} ${tag} PARENT_SCOPE) +endfunction() + + +function(_c4_extract_version_tag tag_) + # git describe --tags <commit-id> for unannotated tags + # git describe --contains <commit> +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# set project-wide property +function(c4_set_proj_prop prop value) + c4_dbg("set ${prop}=${value}") + set(C4PROJ_${_c4_prefix}_${prop} ${value}) +endfunction() + +# set project-wide property +function(c4_get_proj_prop prop var) + c4_dbg("get ${prop}=${C4PROJ_${_c4_prefix}_${prop}}") + set(${var} ${C4PROJ_${_c4_prefix}_${prop}} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# set target-wide c4 property +function(c4_set_target_prop target prop value) + _c4_set_tgt_prop(${target} C4_TGT_${prop} "${value}") +endfunction() +function(c4_append_target_prop target prop value) + _c4_append_tgt_prop(${target} C4_TGT_${prop} "${value}") +endfunction() + +# get target-wide c4 property +function(c4_get_target_prop target prop var) + _c4_get_tgt_prop(val ${target} C4_TGT_${prop}) + set(${var} ${val} PARENT_SCOPE) +endfunction() + + +# get target-wide property +function(_c4_get_tgt_prop out tgt prop) + get_target_property(target_type ${target} TYPE) + if(target_type STREQUAL "INTERFACE_LIBRARY") + get_property(val GLOBAL PROPERTY C4_TGT_${tgt}_${prop}) + else() + get_target_property(val ${tgt} ${prop}) + endif() + c4_dbg("target ${tgt}: get ${prop}=${val}") + set(${out} "${val}" PARENT_SCOPE) +endfunction() + +# set target-wide property +function(_c4_set_tgt_prop tgt prop propval) + c4_dbg("target ${tgt}: set ${prop}=${propval}") + get_target_property(target_type ${target} TYPE) + if(target_type STREQUAL "INTERFACE_LIBRARY") + set_property(GLOBAL PROPERTY C4_TGT_${tgt}_${prop} "${propval}") + else() + set_target_properties(${tgt} PROPERTIES ${prop} "${propval}") + endif() +endfunction() +function(_c4_append_tgt_prop tgt prop propval) + c4_dbg("target ${tgt}: appending ${prop}=${propval}") + _c4_get_tgt_prop(curr ${tgt} ${prop}) + if(curr) + list(APPEND curr "${propval}") + else() + set(curr "${propval}") + endif() + _c4_set_tgt_prop(${tgt} ${prop} "${curr}") +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_set_var_tmp var value) + c4_dbg("tmp-setting ${var} to ${value} (was ${${value}})") + set(_c4_old_val_${var} ${${var}}) + set(${var} ${value} PARENT_SCOPE) +endfunction() + +function(c4_clean_var_tmp var) + c4_dbg("cleaning ${var} to ${_c4_old_val_${var}} (tmp was ${${var}})") + set(${var} ${_c4_old_val_${var}} PARENT_SCOPE) +endfunction() + +macro(c4_override opt val) + set(${opt} ${val} CACHE BOOL "" FORCE) +endmacro() + + +macro(c4_setg var val) + set(${var} ${val}) + set(${var} ${val} PARENT_SCOPE) +endmacro() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_proj_get_version dir tag_o full_o major_o minor_o patch_o tweak_o) + if("${dir}" STREQUAL "") + set(dir ${CMAKE_CURRENT_LIST_DIR}) + endif() + find_program(GIT git REQUIRED) + function(_c4pgv_get_cmd outputvar) + execute_process(COMMAND ${ARGN} + WORKING_DIRECTORY ${dir} + ERROR_VARIABLE error + ERROR_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE output + OUTPUT_STRIP_TRAILING_WHITESPACE) + c4_dbg("output of ${ARGN}: ${outputvar}=${output} [@${dir}]") + set(${outputvar} ${output} PARENT_SCOPE) + endfunction() + # do we have any tags yet? + _c4pgv_get_cmd(head_desc ${GIT} describe HEAD) + _c4pgv_get_cmd(branch ${GIT} rev-parse --abbrev-ref HEAD) + if(NOT head_desc) + c4_dbg("the repo does not have any tags yet") + _c4pgv_get_cmd(commit_hash ${GIT} rev-parse --short HEAD) + set(otag "${commit_hash}-${branch}") + else() + c4_dbg("there are tags!") + # is the current commit tagged? + _c4pgv_get_cmd(commit_hash_full ${GIT} rev-parse HEAD) + _c4pgv_get_cmd(commit_desc ${GIT} describe --exact-match ${commit_hash_full}) + if(commit_desc) + c4_dbg("current commit is tagged") + # is the tag a version tag? + _c4_parse_version_tag(${commit_desc} is_version major minor patch tweak more) + if(is_version) + c4_dbg("current commit's tag is a version tag") + # is the tag the current version tag? + if("${is_version}" VERSION_EQUAL "${${_c4_prefix}_VERSION_FULL}") + c4_dbg("this is the official version commit") + else() + c4_dbg("this is a different version") + endif() + set(otag "${commit_desc}") + else() + c4_dbg("this is a non-version tag") + set(otag "${commit_desc}-${branch}") + endif() + else(commit_desc) + # is the latest tag in the head_desc a version tag? + string(REGEX REPLACE "(.*)-[0-9]+-[0-9a-f]+" "\\1" latest_tag "${head_desc}") + c4_dbg("current commit is NOT tagged. latest tag=${latest_tag}") + _c4_parse_version_tag(${latest_tag} latest_tag_is_a_version major minor patch tweak more) + if(latest_tag_is_a_version) + c4_dbg("latest tag is a version. stick to the head description") + set(otag "${head_desc}-${branch}") + set(full "${latest_tag_is_a_version}") + else() + c4_dbg("latest tag is NOT a version. Use the current project version from cmake + the output of git describe") + set(otag "v${full}-${head_desc}-${branch}") + set(full "${${_c4_prefix}_VERSION_FULL}") + set(major "${${_c4_prefix}_VERSION_MAJOR}") + set(minor "${${_c4_prefix}_VERSION_MINOR}") + set(patch "${${_c4_prefix}_VERSION_PATCH}") + set(tweak "${${_c4_prefix}_VERSION_TWEAK}") + endif() + endif(commit_desc) + endif(NOT head_desc) + c4_log("cpack tag: ${otag}") + set(${tag_o} "${otag}" PARENT_SCOPE) + set(${full_o} "${full}" PARENT_SCOPE) + set(${major_o} "${major}" PARENT_SCOPE) + set(${minor_o} "${minor}" PARENT_SCOPE) + set(${patch_o} "${patch}" PARENT_SCOPE) + set(${tweak_o} "${tweak}" PARENT_SCOPE) + # also: dirty index? + # https://stackoverflow.com/questions/2657935/checking-for-a-dirty-index-or-untracked-files-with-git +endfunction() + + +function(_c4_parse_version_tag tag is_version major minor patch tweak more) + # does the tag match a four-part version? + string(REGEX MATCH "v?([0-9]+)([\._][0-9]+)([\._][0-9]+)([\._][0-9]+)(.*)" match "${tag}") + function(_triml arg out) # trim the leading [\._] from the left + if("${arg}" STREQUAL "") + set(${out} "" PARENT_SCOPE) + else() + string(REGEX REPLACE "[\._](.*)" "\\1" ret "${arg}") + set("${out}" "${ret}" PARENT_SCOPE) + endif() + endfunction() + if(match) + set(${is_version} ${tag} PARENT_SCOPE) + _triml("${CMAKE_MATCH_1}" major_v) + _triml("${CMAKE_MATCH_2}" minor_v) + _triml("${CMAKE_MATCH_3}" patch_v) + _triml("${CMAKE_MATCH_4}" tweak_v) + _triml("${CMAKE_MATCH_5}" more_v) + else() + # does the tag match a three-part version? + string(REGEX MATCH "v?([0-9]+)([\._][0-9]+)([\._][0-9]+)(.*)" match "${tag}") + if(match) + set(${is_version} ${tag} PARENT_SCOPE) + _triml("${CMAKE_MATCH_1}" major_v) + _triml("${CMAKE_MATCH_2}" minor_v) + _triml("${CMAKE_MATCH_3}" patch_v) + _triml("${CMAKE_MATCH_4}" more_v) + else() + # does the tag match a two-part version? + string(REGEX MATCH "v?([0-9]+)([\._][0-9]+)(.*)" match "${tag}") + if(match) + set(${is_version} ${tag} PARENT_SCOPE) + _triml("${CMAKE_MATCH_1}" major_v) + _triml("${CMAKE_MATCH_2}" minor_v) + _triml("${CMAKE_MATCH_3}" more_v) + else() + # not a version! + set(${is_version} FALSE PARENT_SCOPE) + endif() + endif() + endif() + set(${major} "${major_v}" PARENT_SCOPE) + set(${minor} "${minor_v}" PARENT_SCOPE) + set(${patch} "${patch_v}" PARENT_SCOPE) + set(${tweak} "${tweak_v}" PARENT_SCOPE) + set(${more} "${more_v}" PARENT_SCOPE) +endfunction() + + +#function(testvtag) +# set(err FALSE) +# function(cmp value expected) +# if(NOT ("${${value}}" STREQUAL "${expected}")) +# c4_log("${tag}: error: expected ${value}=='${expected}': '${${value}}'=='${expected}'") +# set(err TRUE PARENT_SCOPE) +# else() +# c4_log("${tag}: ok: expected ${value}=='${expected}': '${${value}}'=='${expected}'") +# endif() +# endfunction() +# function(verify tag is_version_e major_e minor_e patch_e tweak_e more_e) +# _c4_parse_version_tag(${tag} is_version major minor patch tweak more) +# cmp(is_version ${is_version_e}) +# cmp(major "${major_e}") +# cmp(minor "${minor_e}") +# cmp(patch "${patch_e}") +# cmp(tweak "${tweak_e}") +# cmp(more "${more_e}") +# set(err ${err} PARENT_SCOPE) +# endfunction() +# verify(v12.34.567.89-rcfoo TRUE 12 34 567 89 -rcfoo) +# verify(v12_34_567_89-rcfoo TRUE 12 34 567 89 -rcfoo) +# verify(v12.34.567.89 TRUE 12 34 567 89 "") +# verify(v12_34_567_89 TRUE 12 34 567 89 "") +# verify(v12.34.567-rcfoo TRUE 12 34 567 "" -rcfoo) +# verify(v12_34_567-rcfoo TRUE 12 34 567 "" -rcfoo) +# verify(v12.34.567 TRUE 12 34 567 "" "") +# verify(v12_34_567 TRUE 12 34 567 "" "") +# verify(v12_34 TRUE 12 34 "" "" "") +# verify(v12.34 TRUE 12 34 "" "" "") +# if(err) +# c4_err("test failed") +# endif() +#endfunction() +#testvtag() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + + +macro(_c4_handle_cxx_standard_args) + # EXTENSIONS: + # enable compiler extensions eg, prefer gnu++11 to c++11 + if(EXTENSIONS IN_LIST ARGN) + set(_EXTENSIONS ON) + else() + c4_get_from_first_of(_EXTENSIONS + ENV + DEFAULT OFF + VARS ${_c4_uprefix}CXX_EXTENSIONS C4_CXX_EXTENSIONS CMAKE_CXX_EXTENSIONS) + endif() + # + # OPTIONAL + if(OPTIONAL IN_LIST ARGN) + set(_REQUIRED OFF) + else() + c4_get_from_first_of(_REQUIRED + ENV + DEFAULT ON + VARS ${_c4_uprefix}CXX_STANDARD_REQUIRED C4_CXX_STANDARD_REQUIRED CMAKE_CXX_STANDARD_REQUIRED) + endif() +endmacro() + + +# set the global cxx standard for the project. +# +# examples: +# c4_set_cxx(latest) # find the latest standard supported by the compiler, and use that +# c4_set_cxx(11) # required, no extensions (eg c++11) +# c4_set_cxx(14) # required, no extensions (eg c++14) +# c4_set_cxx(11 EXTENSIONS) # opt-in to extensions (eg, gnu++11) +# c4_set_cxx(14 EXTENSIONS) # opt-in to extensions (eg, gnu++14) +# c4_set_cxx(11 OPTIONAL) # not REQUIRED. no extensions +# c4_set_cxx(11 OPTIONAL EXTENSIONS) # not REQUIRED. with extensions. +macro(c4_set_cxx standard) + _c4_handle_cxx_standard_args(${ARGN}) + if(NOT DEFINED CMAKE_CXX_STANDARD) + c4_log("setting C++ standard: ${standard}") + c4_setg(CMAKE_CXX_STANDARD ${standard}) + endif() + if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED) + c4_log("setting C++ standard required: ${_REQUIRED}") + c4_setg(CMAKE_CXX_STANDARD_REQUIRED ${_REQUIRED}) + endif() + if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED) + c4_log("setting C++ standard extensions: ${_EXTENSIONS}") + c4_setg(CMAKE_CXX_EXTENSIONS ${_EXTENSIONS}) + endif() +endmacro() + + +# set the cxx standard for a target. +# +# examples: +# c4_target_set_cxx(target latest) # find the latest standard supported by the compiler, and use that +# c4_target_set_cxx(target 11) # required, no extensions (eg c++11) +# c4_target_set_cxx(target 14) # required, no extensions (eg c++14) +# c4_target_set_cxx(target 11 EXTENSIONS) # opt-in to extensions (eg, gnu++11) +# c4_target_set_cxx(target 14 EXTENSIONS) # opt-in to extensions (eg, gnu++14) +# c4_target_set_cxx(target 11 OPTIONAL) # not REQUIRED. no extensions +# c4_target_set_cxx(target 11 OPTIONAL EXTENSIONS) +function(c4_target_set_cxx target standard) + c4_dbg("setting C++ standard for target ${target}: ${standard}") + _c4_handle_cxx_standard_args(${ARGN}) + set_target_properties(${target} PROPERTIES + CXX_STANDARD ${standard} + CXX_STANDARD_REQUIRED ${_REQUIRED} + CXX_EXTENSIONS ${_EXTENSIONS}) + target_compile_features(${target} PUBLIC cxx_std_${standard}) +endfunction() + + +# set the cxx standard for a target based on the global project settings +function(c4_target_inherit_cxx_standard target) + c4_dbg("inheriting C++ standard for target ${target}: ${CMAKE_CXX_STANDARD}") + set_target_properties(${target} PROPERTIES + CXX_STANDARD "${CMAKE_CXX_STANDARD}" + CXX_STANDARD_REQUIRED "${CMAKE_CXX_STANDARD_REQUIRED}" + CXX_EXTENSIONS "${CMAKE_CXX_EXTENSIONS}") + target_compile_features(${target} PUBLIC cxx_std_${CMAKE_CXX_STANDARD}) +endfunction() + + +function(_c4_find_latest_supported_cxx_standard out) + if(NOT c4_latest_supported_cxx_standard) + include(CheckCXXCompilerFlag) + # make sure CMAKE_CXX_FLAGS is clean here + # see https://cmake.org/cmake/help/v3.16/module/CheckCXXCompilerFlag.html + # Note: since this is a function, we don't need to reset CMAKE_CXX_FLAGS + # back to its previous value + set(CMAKE_CXX_FLAGS) + set(standard 11) # default to C++11 if everything fails + foreach(s ${C4_CXX_STANDARDS}) + if(MSVC) + set(flag /std:c++${s}) + else() + # assume GNU-style compiler + set(flag -std=c++${s}) + endif() + c4_log("checking CXX standard: C++${s} flag=${flag}") + check_cxx_compiler_flag(${flag} has${s}) + if(has${s}) + c4_log("checking CXX standard: C++${s} is supported! flag=${flag}") + set(standard ${s}) + break() + else() + c4_log("checking CXX standard: C++${s}: no support for flag=${flag} no") + endif() + endforeach() + set(c4_latest_supported_cxx_standard ${standard} CACHE INTERNAL "") + endif() + set(${out} ${c4_latest_supported_cxx_standard} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +# examples: +# +# # require subproject c4core, as a subdirectory. c4core will be used +# # as a separate library +# c4_require_subproject(c4core SUBDIRECTORY ${C4OPT_EXT_DIR}/c4core) +# +# # require subproject c4core, as a remote proj +# c4_require_subproject(c4core REMOTE +# GIT_REPOSITORY https://github.com/biojppm/c4core +# GIT_TAG master +# ) +function(c4_require_subproject subproj) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + INCORPORATE + EXCLUDE_FROM_ALL + _ARGS1 + SUBDIRECTORY # the subproject is located in the given directory name and + # will be added via add_subdirectory() + _ARGSN + REMOTE # the subproject is located in a remote repo/url + # and will be added via c4_import_remote_proj(), + # forwarding all the arguments in here. + OVERRIDE # a list of variable name+value pairs + # these variables will be set with c4_override() + # before calling add_subdirectory() + SET_FOLDER_TARGETS # Set the folder of the given targets using + # c4_set_folder_remote_project_targets(). + # The first expected argument is the folder, + # and the remaining arguments are the targets + # which we want to set the folder. + _DEPRECATE + INTERFACE + ) + list(APPEND _${_c4_uprefix}_deps ${subproj}) + c4_setg(_${_c4_uprefix}_deps ${_${_c4_uprefix}_deps}) + c4_dbg("-----------------------------------------------") + c4_dbg("requires subproject ${subproj}!") + if(_INCORPORATE) + c4_dbg("requires subproject ${subproj} in INCORPORATE mode!") + c4_dbg_var(${_c4_root_uproj}_STANDALONE) + if(${_c4_root_uproj}_STANDALONE) + c4_dbg("${_c4_root_uproj} is STANDALONE: honoring INCORPORATE mode...") + else() + c4_dbg("${_c4_root_uproj} is not STANDALONE: ignoring INCORPORATE mode...") + set(_INCORPORATE OFF) + endif() + endif() + # + _c4_get_subproject_property(${subproj} AVAILABLE _available) + if(_available) + c4_dbg("required subproject ${subproj} was already imported:") + c4_dbg_subproject(${subproj}) + # TODO check version compatibility + else() #elseif(NOT _${subproj}_available) + c4_dbg("required subproject ${subproj} is unknown. Importing...") + if(_EXCLUDE_FROM_ALL) + set(excl EXCLUDE_FROM_ALL) + endif() + # forward c4 compile flags + string(TOUPPER ${subproj} usubproj) + c4_setg(${usubproj}_CXX_FLAGS_FWD "${${_c4_uprefix}CXX_FLAGS}") + c4_setg(${usubproj}_CXX_FLAGS_OPT_FWD "${${_c4_uprefix}CXX_FLAGS_OPT}") + c4_setg(${usubproj}_CXX_LINKER_FLAGS_FWD "${${_c4_uprefix}CXX_LINKER_FLAGS}") + # root dir + set(_r ${CMAKE_CURRENT_BINARY_DIR}/subprojects/${subproj}) + if(_REMOTE) + c4_log("importing subproject ${subproj} (REMOTE)... ${_REMOTE}") + _c4_mark_subproject_imported(${subproj} ${_r}/src ${_r}/build ${_INCORPORATE}) + c4_import_remote_proj(${subproj} ${_r} REMOTE ${_REMOTE} OVERRIDE ${_OVERRIDE} ${excl}) + _c4_get_subproject_property(${subproj} SRC_DIR _srcdir) + c4_dbg("finished importing subproject ${subproj} (REMOTE, SRC_DIR=${_srcdir}).") + elseif(_SUBDIRECTORY) + c4_log("importing subproject ${subproj} (SUBDIRECTORY)... ${_SUBDIRECTORY}") + _c4_mark_subproject_imported(${subproj} ${_SUBDIRECTORY} ${_r}/build ${_INCORPORATE}) + c4_add_subproj(${subproj} ${_SUBDIRECTORY} ${_r}/build OVERRIDE ${_OVERRIDE} ${excl}) + set(_srcdir ${_SUBDIRECTORY}) + c4_dbg("finished importing subproject ${subproj} (SUBDIRECTORY=${_SUBDIRECTORY}).") + else() + c4_err("subproject type must be either REMOTE or SUBDIRECTORY") + endif() + endif() + # + if(_SET_FOLDER_TARGETS) + c4_set_folder_remote_project_targets(${_SET_FOLDER_TARGETS}) + endif() +endfunction(c4_require_subproject) + + +function(c4_add_subproj proj dir bindir) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + EXCLUDE_FROM_ALL # forward to add_subdirectory() + _ARGS1 + _ARGSN + OVERRIDE # a list of variable name+value pairs + # these variables will be set with c4_override() + # before calling add_subdirectory() + ) + # push the subproj into the current path + set(prev_subproject ${_c4_curr_subproject}) + set(prev_path ${_c4_curr_path}) + set(_c4_curr_subproject ${proj}) + string(REGEX MATCH ".*/${proj}\$" pos "${_c4_curr_path}") + if(pos EQUAL -1) + string(REGEX MATCH "^${proj}\$" pos "${_c4_curr_path}") + if(pos EQUAL -1) + set(_c4_curr_path ${_c4_curr_path}/${proj}) + endif() + endif() + # + while(_OVERRIDE) + list(POP_FRONT _OVERRIDE varname) + list(POP_FRONT _OVERRIDE varvalue) + c4_override(${varname} ${varvalue}) + endwhile() + # + if(_EXCLUDE_FROM_ALL) + set(excl EXCLUDE_FROM_ALL) + endif() + # + c4_dbg("adding subproj: ${prev_subproject}->${_c4_curr_subproject}. path=${_c4_curr_path}") + add_subdirectory(${dir} ${bindir} ${excl}) + # pop the subproj from the current path + set(_c4_curr_subproject ${prev_subproject}) + set(_c4_curr_path ${prev_path}) +endfunction() + + +function(_c4_mark_subproject_imported subproject_name subproject_src_dir subproject_bin_dir incorporate) + c4_dbg("marking subproject imported: ${subproject_name} (imported by ${_c4_prefix}). src=${subproject_src_dir}") + _c4_append_subproject_property(${_c4_prefix} DEPENDENCIES ${subproject_name}) + _c4_get_folder(folder ${_c4_prefix} ${subproject_name}) + _c4_set_subproject_property(${subproject_name} AVAILABLE ON) + _c4_set_subproject_property(${subproject_name} IMPORTER "${_c4_prefix}") + _c4_set_subproject_property(${subproject_name} SRC_DIR "${subproject_src_dir}") + _c4_set_subproject_property(${subproject_name} BIN_DIR "${subproject_bin_dir}") + _c4_set_subproject_property(${subproject_name} FOLDER "${folder}") + _c4_set_subproject_property(${subproject_name} INCORPORATE "${incorporate}") +endfunction() + + +function(_c4_get_subproject_property subproject property var) + get_property(v GLOBAL PROPERTY _c4_subproject-${subproject}-${property}) + set(${var} "${v}" PARENT_SCOPE) +endfunction() + + +function(_c4_set_subproject_property subproject property value) + c4_dbg("setting subproj prop: ${subproject}: ${property}=${value}") + set_property(GLOBAL PROPERTY _c4_subproject-${subproject}-${property} "${value}") +endfunction() +function(_c4_append_subproject_property subproject property value) + _c4_get_subproject_property(${subproject} ${property} cval) + if(cval) + list(APPEND cval ${value}) + else() + set(cval ${value}) + endif() + _c4_set_subproject_property(${subproject} ${property} ${cval}) +endfunction() + + +function(_c4_is_incorporated subproj out) + if("${subproj}" STREQUAL "${_c4_root_proj}") + c4_dbg("${subproj} is incorporated? root proj, no") + set(${out} OFF PARENT_SCOPE) + else() + _c4_get_subproject_property(${subproj} INCORPORATE inc) + c4_dbg("${subproj} is incorporated? not root proj, incorporate=${inc}") + set(${out} ${inc} PARENT_SCOPE) + endif() +endfunction() + + +function(c4_dbg_subproject subproject) + set(props AVAILABLE IMPORTER SRC_DIR BIN_DIR DEPENDENCIES FOLDER INCORPORATE) + foreach(p ${props}) + _c4_get_subproject_property(${subproject} ${p} pv) + c4_dbg("${subproject}: ${p}=${pv}") + endforeach() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# +# +function(c4_import_remote_proj name dir) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + EXCLUDE_FROM_ALL + _ARGS1 + SUBDIR # path to the subdirectory where the CMakeLists file is to be found. + _ARGSN + OVERRIDE # a list of variable name+value pairs + # these variables will be set with c4_override() + # before calling add_subdirectory() + REMOTE # to specify url, repo, tag, or branch, + # pass the needed arguments after dir. + # These arguments will be forwarded to ExternalProject_Add() + SET_FOLDER_TARGETS # Set the folder of the given targets using + # c4_set_folder_remote_project_targets(). + # The first expected argument is the folder, + # and the remaining arguments are the targets + # which we want to set the folder. + ) + set(srcdir_in_out "${dir}") + c4_download_remote_proj(${name} srcdir_in_out ${_REMOTE}) + if(_SUBDIR) + set(srcdir_in_out "${srcdir_in_out}/${_SUBDIR}") + endif() + _c4_set_subproject_property(${name} SRC_DIR "${srcdir_in_out}") + if(_EXCLUDE_FROM_ALL) + set(excl EXCLUDE_FROM_ALL) + endif() + c4_add_subproj(${name} "${srcdir_in_out}" "${dir}/build" OVERRIDE ${_OVERRIDE} ${excl}) + # + if(_SET_FOLDER_TARGETS) + c4_set_folder_remote_project_targets(${_SET_FOLDER_TARGETS}) + endif() +endfunction() + + +# download remote projects while running cmake +# to specify url, repo, tag, or branch, +# pass the needed arguments after dir. +# These arguments will be forwarded to ExternalProject_Add() +function(c4_download_remote_proj name candidate_dir) + # https://crascit.com/2015/07/25/cmake-gtest/ + # (via https://stackoverflow.com/questions/15175318/cmake-how-to-build-external-projects-and-include-their-targets) + set(dir ${${candidate_dir}}) + if("${dir}" STREQUAL "") + set(dir "${CMAKE_BINARY_DIR}/extern/${name}") + endif() + set(cvar _${_c4_uprefix}_DOWNLOAD_${name}_LOCATION) + set(cval ${${cvar}}) + # + # was it already downloaded in this project? + if(NOT ("${cval}" STREQUAL "")) + if(EXISTS "${cval}") + c4_log("${name} was previously imported into this project - found at \"${cval}\"!") + set(${candidate_dir} "${cval}" PARENT_SCOPE) + return() + else() + c4_log("${name} was previously imported into this project - but was NOT found at \"${cval}\"!") + endif() + endif() + # + # try to find an existing version (downloaded by some other project) + set(out "${dir}") + _c4_find_cached_proj(${name} out) + if(NOT ("${out}" STREQUAL "${dir}")) + c4_log("using ${name} from \"${out}\"...") + set(${cvar} "${out}" CACHE INTERNAL "") + set(${candidate_dir} "${out}" PARENT_SCOPE) + return() + endif() + # + # no version was found; need to download. + c4_log("downloading ${name}: not in cache...") + # check for a global place to download into + set(srcdir) + _c4_get_cached_srcdir_global_extern(${name} srcdir) + if("${srcdir}" STREQUAL "") + # none found; default to the given dir + set(srcdir "${dir}/src") + endif() + # + # do it + #if((EXISTS ${dir}/dl) AND (EXISTS ${dir}/dl/CMakeLists.txt)) + # return() + #endif() + c4_log("downloading remote project: ${name} -> \"${srcdir}\" (dir=${dir})...") + # + file(WRITE ${dir}/dl/CMakeLists.txt " +cmake_minimum_required(VERSION 2.8.2) +project(${_c4_lcprefix}-download-${name} NONE) + +# this project only downloads ${name} +# (ie, no configure, build or install step) +include(ExternalProject) + +ExternalProject_Add(${name}-dl + ${ARGN} + SOURCE_DIR \"${srcdir}\" + BINARY_DIR \"${dir}/build\" + CONFIGURE_COMMAND \"\" + BUILD_COMMAND \"\" + INSTALL_COMMAND \"\" + TEST_COMMAND \"\" +) +") + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . + WORKING_DIRECTORY ${dir}/dl) + execute_process(COMMAND ${CMAKE_COMMAND} --build . + WORKING_DIRECTORY ${dir}/dl) + # + set(${candidate_dir} "${srcdir}" PARENT_SCOPE) + set(_${_c4_uprefix}_DOWNLOAD_${name}_LOCATION "${srcdir}" CACHE INTERNAL "") +endfunction() + + +# checks if the project was already downloaded. If it was, then dir_in_out is +# changed to the directory where the project was found at. +function(_c4_find_cached_proj name dir_in_out) + c4_log("downloading ${name}: searching cached project...") + # + # 1. search in the per-import variable, eg RYML_CACHE_DOWNLOAD_GTEST + string(TOUPPER ${name} uname) + set(var ${_c4_uprefix}CACHE_DOWNLOAD_${uname}) + set(val "${${var}}") + if(NOT ("${val}" STREQUAL "")) + c4_log("downloading ${name}: searching in ${var}=${val}") + if(EXISTS "${val}") + c4_log("downloading ${name}: picked ${sav} instead of ${${dir_in_out}}") + set(${dir_in_out} ${sav} PARENT_SCOPE) + endif() + endif() + # + # 2. search in the global directory (if there is one) + _c4_get_cached_srcdir_global_extern(${name} sav) + if(NOT ("${sav}" STREQUAL "")) + c4_log("downloading ${name}: searching in C4_EXTERN_DIR: ${sav}") + if(EXISTS "${sav}") + c4_log("downloading ${name}: picked ${sav} instead of ${${dir_in_out}}") + set(${dir_in_out} ${sav} PARENT_SCOPE) + endif() + endif() +endfunction() + + +function(_c4_get_cached_srcdir_global_extern name out) + set(${out} "" PARENT_SCOPE) + if("${C4_EXTERN_DIR}" STREQUAL "") + set(C4_EXTERN_DIR "$ENV{C4_EXTERN_DIR}") + endif() + if(NOT ("${C4_EXTERN_DIR}" STREQUAL "")) + set(${out} "${C4_EXTERN_DIR}/${name}" PARENT_SCOPE) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(_c4_get_folder output importer_subproject subproject_name) + _c4_get_subproject_property(${importer_subproject} FOLDER importer_folder) + if("${importer_folder}" STREQUAL "") + set(folder ${importer_subproject}) + else() + set(folder "${importer_folder}/deps/${subproject_name}") + endif() + set(${output} ${folder} PARENT_SCOPE) +endfunction() + + +function(_c4_set_target_folder target subfolder) + string(FIND "${subfolder}" "/" pos) + if(pos EQUAL 0) + if("${_c4_curr_path}" STREQUAL "") + string(SUBSTRING "${subfolder}" 1 -1 sf) + set_target_properties(${target} PROPERTIES + FOLDER "${sf}") + else() + set_target_properties(${target} PROPERTIES + FOLDER "${subfolder}") + endif() + elseif("${subfolder}" STREQUAL "") + set_target_properties(${target} PROPERTIES + FOLDER "${_c4_curr_path}") + else() + if("${_c4_curr_path}" STREQUAL "") + set_target_properties(${target} PROPERTIES + FOLDER "${subfolder}") + else() + set_target_properties(${target} PROPERTIES + FOLDER "${_c4_curr_path}/${subfolder}") + endif() + endif() +endfunction() + + +function(c4_set_folder_remote_project_targets subfolder) + foreach(target ${ARGN}) + if(TARGET ${target}) + _c4_set_target_folder(${target} "${subfolder}") + endif() + endforeach() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# a convenience alias to c4_add_target() +function(c4_add_executable target) + c4_add_target(${target} EXECUTABLE ${ARGN}) +endfunction(c4_add_executable) + + +# a convenience alias to c4_add_target() +function(c4_add_library target) + c4_add_target(${target} LIBRARY ${ARGN}) +endfunction(c4_add_library) + + +# example: c4_add_target(ryml LIBRARY SOURCES ${SRC}) +function(c4_add_target target) + c4_dbg("adding target: ${target}: ${ARGN}") + set(opt0arg + LIBRARY # the target is a library + EXECUTABLE # the target is an executable + WIN32 # the executable is WIN32 + SANITIZE # deprecated + ) + set(opt1arg + LIBRARY_TYPE # override global setting for C4_LIBRARY_TYPE + SHARED_MACRO # the name of the macro to turn on export/import symbols + # for compiling the library as a windows DLL. + # defaults to ${_c4_uprefix}SHARED. + SHARED_EXPORTS # the name of the macro to turn on export of symbols + # for compiling the library as a windows DLL. + # defaults to ${_c4_uprefix}EXPORTS. + SOURCE_ROOT # the directory where relative source paths + # should be resolved. when empty, + # use CMAKE_CURRENT_SOURCE_DIR + FOLDER # IDE folder to group the target in + SANITIZERS # outputs the list of sanitize targets in this var + SOURCE_TRANSFORM # WIP + ) + set(optnarg + INCORPORATE # incorporate these libraries into this target, + # subject to ${_c4_uprefix}STANDALONE and C4_STANDALONE + SOURCES PUBLIC_SOURCES INTERFACE_SOURCES PRIVATE_SOURCES + HEADERS PUBLIC_HEADERS INTERFACE_HEADERS PRIVATE_HEADERS + INC_DIRS PUBLIC_INC_DIRS INTERFACE_INC_DIRS PRIVATE_INC_DIRS + LIBS PUBLIC_LIBS INTERFACE_LIBS PRIVATE_LIBS + DEFS PUBLIC_DEFS INTERFACE_DEFS PRIVATE_DEFS # defines + CFLAGS PUBLIC_CFLAGS INTERFACE_CFLAGS PRIVATE_CFLAGS # compiler flags. TODO: linker flags + DLLS # DLLs required by this target + MORE_ARGS + ) + cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN}) + # + if(_SANITIZE) + c4_err("SANITIZE is deprecated") + endif() + + if(${_LIBRARY}) + set(_what LIBRARY) + elseif(${_EXECUTABLE}) + set(_what EXECUTABLE) + else() + c4_err("must be either LIBRARY or EXECUTABLE") + endif() + + _c4_handle_arg(SHARED_MACRO ${_c4_uprefix}MACRO) + _c4_handle_arg(SHARED_EXPORTS ${_c4_uprefix}EXPORTS) + _c4_handle_arg_or_fallback(SOURCE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}") + function(_c4_transform_to_full_path list all) + set(l) + foreach(f ${${list}}) + if(NOT IS_ABSOLUTE "${f}") + set(f "${_SOURCE_ROOT}/${f}") + endif() + list(APPEND l "${f}") + endforeach() + set(${list} "${l}" PARENT_SCOPE) + set(cp ${${all}}) + list(APPEND cp ${l}) + set(${all} ${cp} PARENT_SCOPE) + endfunction() + _c4_transform_to_full_path( _SOURCES allsrc) + _c4_transform_to_full_path( _HEADERS allsrc) + _c4_transform_to_full_path( _PUBLIC_SOURCES allsrc) + _c4_transform_to_full_path(_INTERFACE_SOURCES allsrc) + _c4_transform_to_full_path( _PRIVATE_SOURCES allsrc) + _c4_transform_to_full_path( _PUBLIC_HEADERS allsrc) + _c4_transform_to_full_path(_INTERFACE_HEADERS allsrc) + _c4_transform_to_full_path( _PRIVATE_HEADERS allsrc) + create_source_group("" "${_SOURCE_ROOT}" "${allsrc}") + # is the target name prefixed with the project prefix? + string(REGEX MATCH "${_c4_prefix}::.*" target_is_prefixed "${target}") + if(NOT ${_c4_uprefix}SANITIZE_ONLY) + if(${_EXECUTABLE}) + c4_dbg("adding executable: ${target}") + if(WIN32) + if(${_WIN32}) + list(APPEND _MORE_ARGS WIN32) + endif() + endif() + add_executable(${target} ${_MORE_ARGS}) + if(NOT target_is_prefixed) + add_executable(${_c4_prefix}::${target} ALIAS ${target}) + endif() + set(src_mode PRIVATE) + set(tgt_type PUBLIC) + set(compiled_target ON) + set_target_properties(${target} PROPERTIES VERSION ${${_c4_prefix}_VERSION}) + elseif(${_LIBRARY}) + c4_dbg("adding library: ${target}") + set(_blt ${C4_LIBRARY_TYPE}) # build library type + if(NOT "${_LIBRARY_TYPE}" STREQUAL "") + set(_blt ${_LIBRARY_TYPE}) + endif() + if("${_blt}" STREQUAL "") + endif() + # + if("${_blt}" STREQUAL "INTERFACE") + c4_dbg("adding interface library ${target}") + add_library(${target} INTERFACE) + set(src_mode INTERFACE) + set(tgt_type INTERFACE) + set(compiled_target OFF) + else() + if("${_blt}" STREQUAL "") + # obey BUILD_SHARED_LIBS (ie, either static or shared library) + c4_dbg("adding library ${target} (defer to BUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}) --- ${_MORE_ARGS}") + add_library(${target} ${_MORE_ARGS}) + if(BUILD_SHARED_LIBS) + set(_blt SHARED) + else() + set(_blt STATIC) + endif() + else() + c4_dbg("adding library ${target} with type ${_blt}") + add_library(${target} ${_blt} ${_MORE_ARGS}) + endif() + # libraries + set(src_mode PRIVATE) + set(tgt_type PUBLIC) + set(compiled_target ON) + set_target_properties(${target} PROPERTIES VERSION ${${_c4_prefix}_VERSION}) + if("${_blt}" STREQUAL SHARED) + set_target_properties(${target} PROPERTIES SOVERSION ${${_c4_prefix}_VERSION}) + endif() + # exports for shared libraries + if(WIN32) + if("${_blt}" STREQUAL SHARED) + set_target_properties(${target} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) + target_compile_definitions(${target} PUBLIC ${_SHARED_MACRO}) + target_compile_definitions(${target} PRIVATE $<BUILD_INTERFACE:${_SHARED_EXPORTS}>) + # save the name of the macro for later use when(if) incorporating this library + c4_set_target_prop(${target} SHARED_EXPORTS ${_SHARED_EXPORTS}) + endif() # shared lib + endif() # win32 + endif() # interface or lib + if(NOT target_is_prefixed) + add_library(${_c4_prefix}::${target} ALIAS ${target}) + endif() + endif(${_EXECUTABLE}) + + if(src_mode STREQUAL "PUBLIC") + c4_add_target_sources(${target} + PUBLIC "${_SOURCES};${_HEADERS};${_PUBLIC_SOURCES};${_PUBLIC_HEADERS}" + INTERFACE "${_INTERFACE_SOURCES};${_INTERFACE_HEADERS}" + PRIVATE "${_PRIVATE_SOURCES};${_PRIVATE_HEADERS}") + elseif(src_mode STREQUAL "INTERFACE") + c4_add_target_sources(${target} + PUBLIC "${_PUBLIC_SOURCES};${_PUBLIC_HEADERS}" + INTERFACE "${_SOURCES};${_HEADERS};${_INTERFACE_SOURCES};${_INTERFACE_HEADERS}" + PRIVATE "${_PRIVATE_SOURCES};${_PRIVATE_HEADERS}") + elseif(src_mode STREQUAL "PRIVATE") + c4_add_target_sources(${target} + PUBLIC "${_PUBLIC_SOURCES};${_PUBLIC_HEADERS}" + INTERFACE "${_INTERFACE_SOURCES};${_INTERFACE_HEADERS}" + PRIVATE "${_SOURCES};${_HEADERS};${_PRIVATE_SOURCES};${_PRIVATE_HEADERS}") + elseif() + c4_err("${target}: adding sources: invalid source mode") + endif() + _c4_set_tgt_prop(${target} C4_SOURCE_ROOT "${_SOURCE_ROOT}") + + if(_INC_DIRS) + c4_dbg("${target}: adding include dirs ${_INC_DIRS} [from target: ${tgt_type}]") + target_include_directories(${target} "${tgt_type}" ${_INC_DIRS}) + endif() + if(_PUBLIC_INC_DIRS) + c4_dbg("${target}: adding PUBLIC include dirs ${_PUBLIC_INC_DIRS}") + target_include_directories(${target} PUBLIC ${_PUBLIC_INC_DIRS}) + endif() + if(_INTERFACE_INC_DIRS) + c4_dbg("${target}: adding INTERFACE include dirs ${_INTERFACE_INC_DIRS}") + target_include_directories(${target} INTERFACE ${_INTERFACE_INC_DIRS}) + endif() + if(_PRIVATE_INC_DIRS) + c4_dbg("${target}: adding PRIVATE include dirs ${_PRIVATE_INC_DIRS}") + target_include_directories(${target} PRIVATE ${_PRIVATE_INC_DIRS}) + endif() + + if(_LIBS) + _c4_link_with_libs(${target} "${tgt_type}" "${_LIBS}" "${_INCORPORATE}") + endif() + if(_PUBLIC_LIBS) + _c4_link_with_libs(${target} PUBLIC "${_PUBLIC_LIBS}" "${_INCORPORATE}") + endif() + if(_INTERFACE_LIBS) + _c4_link_with_libs(${target} INTERFACE "${_INTERFACE_LIBS}" "${_INCORPORATE}") + endif() + if(_PRIVATE_LIBS) + _c4_link_with_libs(${target} PRIVATE "${_PRIVATE_LIBS}" "${_INCORPORATE}") + endif() + + if(compiled_target) + if(_FOLDER) + _c4_set_target_folder(${target} "${_FOLDER}") + else() + _c4_set_target_folder(${target} "") + endif() + # cxx standard + c4_target_inherit_cxx_standard(${target}) + # compile flags + set(_more_flags + ${${_c4_uprefix}CXX_FLAGS} + ${${_c4_uprefix}C_FLAGS} + ${${_c4_uprefix}CXX_FLAGS_OPT}) + if(_more_flags) + get_target_property(_flags ${target} COMPILE_OPTIONS) + if(_flags) + set(_more_flags ${_flags};${_more_flags}) + endif() + c4_dbg("${target}: COMPILE_FLAGS=${_more_flags}") + target_compile_options(${target} PRIVATE "${_more_flags}") + endif() + # linker flags + set(_link_flags ${${_c4_uprefix}CXX_LINKER_FLAGS}) + if(_link_flags) + get_target_property(_flags ${target} LINK_OPTIONS) + if(_flags) + set(_link_flags ${_flags};${_more_flags}) + endif() + c4_dbg("${target}: LINKER_FLAGS=${_link_flags}") + target_link_options(${target} PUBLIC "${_link_flags}") + endif() + # static analysis + if(${_c4_uprefix}LINT) + c4_static_analysis_target(${target} "${_FOLDER}" lint_targets) + endif() + endif(compiled_target) + + if(_DEFS) + target_compile_definitions(${target} "${tgt_type}" ${_DEFS}) + endif() + if(_PUBLIC_DEFS) + target_compile_definitions(${target} PUBLIC ${_PUBLIC_DEFS}) + endif() + if(_INTERFACE_DEFS) + target_compile_definitions(${target} INTERFACE ${_INTERFACE_DEFS}) + endif() + if(_PRIVATE_DEFS) + target_compile_definitions(${target} PRIVATE ${_PRIVATE_DEFS}) + endif() + + if(_CFLAGS) + target_compile_options(${target} "${tgt_type}" ${_CFLAGS}) + endif() + if(_PUBLIC_CFLAGS) + target_compile_options(${target} PUBLIC ${_PUBLIC_CFLAGS}) + endif() + if(_INTERFACE_CFLAGS) + target_compile_options(${target} INTERFACE ${_INTERFACE_CFLAGS}) + endif() + if(_PRIVATE_CFLAGS) + target_compile_options(${target} PRIVATE ${_PRIVATE_CFLAGS}) + endif() + + if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND + (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) AND + (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)) + c4_dbg("${target}: adding compat include path") + target_include_directories(${target} PUBLIC $<BUILD_INTERFACE:${_c4_project_dir}/compat>) + endif() + + endif(NOT ${_c4_uprefix}SANITIZE_ONLY) + + if(compiled_target) + if(${_c4_uprefix}SANITIZE) + c4_sanitize_target(${target} + ${_what} # LIBRARY or EXECUTABLE + SOURCES ${allsrc} + INC_DIRS ${_INC_DIRS} ${_PUBLIC_INC_DIRS} ${_INTERFACE_INC_DIRS} ${_PRIVATE_INC_DIRS} + LIBS ${_LIBS} ${_PUBLIC_LIBS} ${_INTERFACE_LIBS} ${_PRIVATE_LIBS} + DEFS ${_DEFS} ${_PUBLIC_DEFS} ${_INTERFACE_DEFS} ${_PRIVATE_DEFS} + CFLAGS ${_CFLAGS} ${_PUBLIC_CFLAGS} ${_INTERFACE_CFLAGS} ${_PRIVATE_CFLAGS} + OUTPUT_TARGET_NAMES san_targets + FOLDER "${_FOLDER}" + ) + endif() + + if(NOT ${_c4_uprefix}SANITIZE_ONLY) + list(INSERT san_targets 0 ${target}) + endif() + + if(_SANITIZERS) + set(${_SANITIZERS} ${san_targets} PARENT_SCOPE) + endif() + + _c4_set_tgt_prop(${target} C4_SAN_TARGETS "${san_targets}") + else() + _c4_set_tgt_prop(${target} C4_SAN_TARGETS "${target}") + endif() + + # gather dlls so that they can be automatically copied to the target directory + if(_DLLS) + c4_append_transitive_property(${target} _C4_DLLS "${_DLLS}") + endif() + + if(${_EXECUTABLE}) + if(WIN32) + c4_get_transitive_property(${target} _C4_DLLS transitive_dlls) + list(REMOVE_DUPLICATES transitive_dlls) + foreach(_dll ${transitive_dlls}) + if(_dll) + c4_dbg("enable copy of dll to target file dir: ${_dll} ---> $<TARGET_FILE_DIR:${target}>") + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_dll}" "$<TARGET_FILE_DIR:${target}>" + ) + else() + message(WARNING "dll required by ${_c4_prefix}/${target} was not found, so cannot copy: ${_dll}") + endif() + endforeach() + endif() + endif() +endfunction() # add_target + + +function(_c4_link_with_libs target link_type libs incorporate) + foreach(lib ${libs}) + # add targets that are DLLs + if(WIN32) + if(TARGET ${lib}) + get_target_property(lib_type ${lib} TYPE) + if(lib_type STREQUAL SHARED_LIBRARY) + c4_append_transitive_property(${target} _C4_DLLS "$<TARGET_FILE:${lib}>") + endif() + endif() + endif() + _c4_lib_is_incorporated(${lib} isinc) + if(isinc OR (incorporate AND ${_c4_uprefix}STANDALONE)) + c4_log("-----> target ${target} ${link_type} incorporating lib ${lib}") + _c4_incorporate_lib(${target} ${link_type} ${lib}) + else() + c4_dbg("${target} ${link_type} linking with lib ${lib}") + target_link_libraries(${target} ${link_type} ${lib}) + endif() + endforeach() +endfunction() + + +function(_c4_lib_is_incorporated lib ret) + c4_dbg("${lib}: is incorporated?") + if(NOT TARGET ${lib}) + c4_dbg("${lib}: no, not a target") + set(${ret} OFF PARENT_SCOPE) + else() + c4_get_target_prop(${lib} INCORPORATING_TARGETS inc) + if(inc) + c4_dbg("${lib}: is incorporated!") + set(${ret} ON PARENT_SCOPE) + else() + c4_dbg("${lib}: is not incorporated!") + set(${ret} OFF PARENT_SCOPE) + endif() + endif() +endfunction() + + +function(_c4_incorporate_lib target link_type lib) + c4_dbg("target ${target}: incorporating lib ${lib} [${link_type}]") + _c4_get_tgt_prop(srcroot ${lib} C4_SOURCE_ROOT) + # + c4_append_target_prop(${lib} INCORPORATING_TARGETS ${target}) + c4_append_target_prop(${target} INCORPORATED_TARGETS ${lib}) + # + _c4_get_tgt_prop(lib_src ${lib} SOURCES) + if(lib_src) + create_source_group("${lib}" "${srcroot}" "${lib_src}") + c4_add_target_sources(${target} INCORPORATED_FROM ${lib} PRIVATE ${lib_src}) + endif() + # + _c4_get_tgt_prop(lib_isrc ${lib} INTERFACE_SOURCES) + if(lib_isrc) + create_source_group("${lib}" "${srcroot}" "${lib_isrc}") + c4_add_target_sources(${target} INCORPORATED_FROM ${lib} INTERFACE ${lib_isrc}) + endif() + # + _c4_get_tgt_prop(lib_psrc ${lib} PRIVATE_SOURCES) + if(lib_psrc) + create_source_group("${lib}" "${srcroot}" "${lib_psrc}") + c4_add_target_sources(${target} INCORPORATED_FROM ${lib} INTERFACE ${lib_psrc}) + endif() + # + # + _c4_get_tgt_prop(lib_incs ${lib} INCLUDE_DIRECTORIES) + if(lib_incs) + target_include_directories(${target} PUBLIC ${lib_incs}) + endif() + # + _c4_get_tgt_prop(lib_iincs ${lib} INTERFACE_INCLUDE_DIRECTORIES) + if(lib_iincs) + target_include_directories(${target} INTERFACE ${lib_iincs}) + endif() + # + # + _c4_get_tgt_prop(lib_lib ${lib} LINK_LIBRARIES) + if(lib_lib) + target_link_libraries(${target} PUBLIC ${lib_lib}) + endif() + _c4_get_tgt_prop(lib_ilib ${lib} INTERFACE_LIBRARY) + if(lib_ilib) + target_link_libraries(${target} INTERFACE ${lib_ilib}) + endif() + # + # + c4_get_target_prop(${lib} SHARED_EXPORTS lib_exports) + if(lib_exports) + target_compile_definitions(${target} PRIVATE $<BUILD_INTERFACE:${lib_exports}>) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +# +# +function(c4_add_target_sources target) + # https://steveire.wordpress.com/2016/08/09/opt-in-header-only-libraries-with-cmake/ + _c4_handle_args(_ARGS ${ARGN} + _ARGS1 # one-value macro arguments + INCORPORATED_FROM + TRANSFORM # Transform types: + # * NONE - do not transform the sources + # * UNITY + # * UNITY_HDR + # * SINGLE_HDR + # * SINGLE_UNIT + _ARGSN # multi-value macro arguments + PUBLIC + INTERFACE + PRIVATE + ) + if(("${_TRANSFORM}" STREQUAL "GLOBAL") OR ("${_TRANSFORM}" STREQUAL "")) + set(_TRANSFORM ${C4_SOURCE_TRANSFORM}) + endif() + if("${_TRANSFORM}" STREQUAL "") + set(_TRANSFORM NONE) + endif() + # + # is this target an interface? + set(_is_iface FALSE) + _c4_get_tgt_prop(target_type ${target} TYPE) + if("${target_type}" STREQUAL "INTERFACE_LIBRARY") + set(_is_iface TRUE) + elseif("${prop_name}" STREQUAL "LINK_LIBRARIES") + set(_is_iface FALSE) + endif() + # + set(out) + set(umbrella ${_c4_lprefix}transform-src) + # + if("${_TRANSFORM}" STREQUAL "NONE") + c4_dbg("target ${target}: source transform: NONE!") + # + # do not transform the sources + # + if(_PUBLIC) + c4_dbg("target=${target} PUBLIC sources: ${_PUBLIC}") + c4_append_target_prop(${target} PUBLIC_SRC "${_PUBLIC}") + if(_INCORPORATED_FROM) + c4_append_target_prop(${target} PUBLIC_SRC_${_INCORPORATED_FROM} "${_PUBLIC}") + else() + c4_append_target_prop(${target} PUBLIC_SRC_${target} "${_PUBLIC}") + endif() + target_sources(${target} PUBLIC "${_PUBLIC}") + endif() + if(_INTERFACE) + c4_dbg("target=${target} INTERFACE sources: ${_INTERFACE}") + c4_append_target_prop(${target} INTERFACE_SRC "${_INTERFACE}") + if(_INCORPORATED_FROM) + c4_append_target_prop(${target} INTERFACE_SRC_${_INCORPORATED_FROM} "${_INTERFACE}") + else() + c4_append_target_prop(${target} INTERFACE_SRC_${target} "${_INTERFACE}") + endif() + target_sources(${target} INTERFACE "${_INTERFACE}") + endif() + if(_PRIVATE) + c4_dbg("target=${target} PRIVATE sources: ${_PRIVATE}") + c4_append_target_prop(${target} PRIVATE_SRC "${_PRIVATE}") + if(_INCORPORATED_FROM) + c4_append_target_prop(${target} PRIVATE_SRC_${_INCORPORATED_FROM} "${_PRIVATE}") + else() + c4_append_target_prop(${target} PRIVATE_SRC_${target} "${_PRIVATE}") + endif() + target_sources(${target} PRIVATE "${_PRIVATE}") + endif() + # + elseif("${_TRANSFORM}" STREQUAL "UNITY") + c4_dbg("target ${target}: source transform: UNITY!") + c4_err("source transformation not implemented") + # + # concatenate all compilation unit files (excluding interface) + # into a single compilation unit + # + _c4cat_filter_srcs("${_PUBLIC}" cpublic) + _c4cat_filter_hdrs("${_PUBLIC}" hpublic) + _c4cat_filter_srcs("${_INTERFACE}" cinterface) + _c4cat_filter_hdrs("${_INTERFACE}" hinterface) + _c4cat_filter_srcs("${_PRIVATE}" cprivate) + _c4cat_filter_hdrs("${_PRIVATE}" hprivate) + if(cpublic OR cinterface OR cprivate) + _c4cat_get_outname(${target} "src" ${C4_GEN_SRC_EXT} out) + c4_dbg("${target}: output unit: ${out}") + c4_cat_sources("${cpublic};${cinterface};${cprivate}" "${out}" ${umbrella}) + add_dependencies(${target} ${out}) + endif() + if(_PUBLIC) + c4_append_target_prop(${target} PUBLIC_SRC + $<BUILD_INTERFACE:${hpublic};${out}> + $<INSTALL_INTERFACE:${hpublic};${out}>) + target_sources(${target} PUBLIC + $<BUILD_INTERFACE:${hpublic};${out}> + $<INSTALL_INTERFACE:${hpublic};${out}>) + endif() + if(_INTERFACE) + c4_append_target_prop(${target} INTERFACE_SRC + $<BUILD_INTERFACE:${hinterface}> + $<INSTALL_INTERFACE:${hinterface}>) + target_sources(${target} INTERFACE + $<BUILD_INTERFACE:${hinterface}> + $<INSTALL_INTERFACE:${hinterface}>) + endif() + if(_PRIVATE) + c4_append_target_prop(${target} PRIVATE_SRC + $<BUILD_INTERFACE:${hprivate}> + $<INSTALL_INTERFACE:${hprivate}>) + target_sources(${target} PRIVATE + $<BUILD_INTERFACE:${hprivate}> + $<INSTALL_INTERFACE:${hprivate}>) + endif() + elseif("${_TRANSFORM}" STREQUAL "UNITY_HDR") + c4_dbg("target ${target}: source transform: UNITY_HDR!") + c4_err("target ${target}: source transformation not implemented") + # + # like unity, but concatenate compilation units into + # a header file, leaving other header files untouched + # + _c4cat_filter_srcs("${_PUBLIC}" cpublic) + _c4cat_filter_hdrs("${_PUBLIC}" hpublic) + _c4cat_filter_srcs("${_INTERFACE}" cinterface) + _c4cat_filter_hdrs("${_INTERFACE}" hinterface) + _c4cat_filter_srcs("${_PRIVATE}" cprivate) + _c4cat_filter_hdrs("${_PRIVATE}" hprivate) + if(c) + _c4cat_get_outname(${target} "src" ${C4_GEN_HDR_EXT} out) + c4_dbg("${target}: output hdr: ${out}") + _c4cat_filter_srcs_hdrs("${_PUBLIC}" c_h) + c4_cat_sources("${c}" "${out}" ${umbrella}) + add_dependencies(${target} ${out}) + add_dependencies(${target} ${_c4_lprefix}cat) + endif() + set(${src} ${out} PARENT_SCOPE) + set(${hdr} ${h} PARENT_SCOPE) + # + elseif("${_TRANSFORM}" STREQUAL "SINGLE_HDR") + c4_dbg("target ${target}: source transform: SINGLE_HDR!") + c4_err("target ${target}: source transformation not implemented") + # + # concatenate everything into a single header file + # + _c4cat_get_outname(${target} "all" ${C4_GEN_HDR_EXT} out) + _c4cat_filter_srcs_hdrs("${_c4al_SOURCES}" ch) + c4_cat_sources("${ch}" "${out}" ${umbrella}) + # + elseif("${_TRANSFORM}" STREQUAL "SINGLE_UNIT") + c4_dbg("target ${target}: source transform: SINGLE_UNIT!") + c4_err("target ${target}: source transformation not implemented") + # + # concatenate: + # * all compilation units into a single compilation unit + # * all headers into a single header + # + _c4cat_get_outname(${target} "src" ${C4_GEN_SRC_EXT} out) + _c4cat_get_outname(${target} "hdr" ${C4_GEN_SRC_EXT} out) + _c4cat_filter_srcs_hdrs("${_c4al_SOURCES}" ch) + c4_cat_sources("${ch}" "${out}" ${umbrella}) + else() + c4_err("unknown transform type: ${transform_type}. Must be one of GLOBAL;NONE;UNITY;TO_HEADERS;SINGLE_HEADER") + endif() +endfunction() + + +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# ----------------------------------------------------------------------------- +# WIP, under construction (still incomplete) +# see: https://github.com/pr0g/cmake-examples +# see: https://cliutils.gitlab.io/modern-cmake/ + + +function(c4_install_target target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS1 # one-value macro arguments + EXPORT # the name of the export target. default: see below. + ) + _c4_handle_arg(EXPORT "${_c4_prefix}-export") + # + c4_dbg("installing target: ${target} ${ARGN}") + #_c4_is_incorporated(${_c4_prefix} inc) + #if(inc) + # c4_dbg("this project is INCORPORATEd. skipping install of targets") + # return() + #endif() + # + _c4_setup_install_vars() + install(TARGETS ${target} + EXPORT ${_EXPORT} + RUNTIME DESTINATION ${_RUNTIME_INSTALL_DIR} #COMPONENT runtime + BUNDLE DESTINATION ${_RUNTIME_INSTALL_DIR} #COMPONENT runtime + LIBRARY DESTINATION ${_LIBRARY_INSTALL_DIR} #COMPONENT runtime + ARCHIVE DESTINATION ${_ARCHIVE_INSTALL_DIR} #COMPONENT development + OBJECTS DESTINATION ${_OBJECTS_INSTALL_DIR} #COMPONENT development + INCLUDES DESTINATION ${_INCLUDE_INSTALL_DIR} #COMPONENT development + PUBLIC_HEADER DESTINATION ${_INCLUDE_INSTALL_DIR} #COMPONENT development + ) + c4_install_sources(${target} include) + # + # on windows, install also required DLLs + if(WIN32) + get_target_property(target_type ${target} TYPE) + if("${target_type}" STREQUAL "EXECUTABLE") + c4_get_transitive_property(${target} _C4_DLLS transitive_dlls) + if(transitive_dlls) + c4_dbg("${target}: installing dlls: ${transitive_dlls} to ${_RUNTIME_INSTALL_DIR}") + list(REMOVE_DUPLICATES transitive_dlls) + install(FILES ${transitive_dlls} + DESTINATION ${_RUNTIME_INSTALL_DIR} # shouldn't it be _LIBRARY_INSTALL_DIR? + #COMPONENT runtime + ) + endif() + endif() + endif() + # + set(l ${${_c4_prefix}_TARGETS}) + list(APPEND l ${target}) + set(${_c4_prefix}_TARGETS ${l} PARENT_SCOPE) + # +# # pkgconfig (WIP) +# set(pc ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/${target}.pc) +# file(WRITE ${pc} "# pkg-config: ${target} +# +#prefix=\"${CMAKE_INSTALL_PREFIX}\" +#exec_prefix=\"\${_c4_prefix}\" +#libdir=\"\${_c4_prefix}/${CMAKE_INSTALL_LIBDIR}\" +#includedir=\"\${_c4_prefix}/include\" +# +#Name: ${target} +#Description: A library for xyzzying frobnixes +#URL: https://github.com/me/mylibrary +#Version: 0.0.0 +#Requires: @PKGCONF_REQ_PUB@ +#Requires.private: @PKGCONF_REQ_PRIV@ +#Cflags: -I\"${includedir}\" +#Libs: -L\"${libdir}\" -lmylibrary +#Libs.private: -L\"${libdir}\" -lmylibrary @PKGCONF_LIBS_PRIV@ +#") +# _c4_setup_install_vars() +# install(FILES ${pc} DESTINATION "${_ARCHIVE_INSTALL_DIR}/pkgconfig/") +endfunction() + + +function(c4_install_sources target destination) + c4_dbg("target ${target}: installing sources to ${destination}") + # executables have no sources requiring install + _c4_get_tgt_prop(target_type ${target} TYPE) + if(target_type STREQUAL "EXECUTABLE") + c4_dbg("target ${target}: is executable, skipping source install") + return() + endif() + # install source from the target and incorporated targets + c4_get_target_prop(${target} INCORPORATED_TARGETS inctargets) + if(inctargets) + set(targets "${inctargets};${target}") + else() + set(targets "${target}") + endif() + foreach(t ${targets}) + _c4_get_tgt_prop(srcroot ${t} C4_SOURCE_ROOT) + # get the sources from the target + # + c4_get_target_prop(${t} PUBLIC_SRC_${t} src) + if(src) + _c4cat_filter_hdrs("${src}" srcf) + _c4cat_filter_additional_exts("${src}" add) + c4_install_files("${srcf}" "${destination}" "${srcroot}") + c4_install_files("${add}" "${destination}" "${srcroot}") + endif() + # + c4_get_target_prop(${t} PRIVATE_SRC_${t} psrc) + if(psrc) + _c4cat_filter_hdrs("${psrc}" psrcf) + _c4cat_filter_additional_exts("${psrc}" add) + c4_install_files("${psrcf}" "${destination}" "${srcroot}") + c4_install_files("${add}" "${destination}" "${srcroot}") + endif() + # + c4_get_target_prop(${t} INTERFACE_SRC_${t} isrc) + if(isrc) + _c4cat_filter_srcs_hdrs("${isrc}" isrcf) + _c4cat_filter_additional_exts("${isrc}" add) + c4_install_files("${isrcf}" "${destination}" "${srcroot}") + c4_install_files("${add}" "${destination}" "${srcroot}") + endif() + # + c4_get_target_prop(${t} ADDFILES addfiles) + if(addfiles) + foreach(af ${addfiles}) + string(REGEX REPLACE "(.*)!!(.*)!!(.*)" "\\1;\\2;\\3" li "${af}") + list(GET li 0 files) + list(GET li 1 dst) + list(GET li 2 relative_to) + string(REPLACE "%%%" ";" files "${files}") + c4_install_files("${files}" "${dst}" "${relative_to}") + endforeach() + endif() + # + c4_get_target_prop(${t} ADDDIRS adddirs) + if(adddirs) + foreach(af ${adddirs}) + string(REGEX REPLACE "(.*)!!(.*)!!(.*)" "\\1;\\2;\\3" li "${af}") + list(GET li 0 dirs) + list(GET li 1 dst) + list(GET li 2 relative_to) + string(REPLACE "%%%" ";" dirs "${files}") + c4_install_dirs("${dirs}" "${dst}" "${relative_to}") + endforeach() + endif() + endforeach() +endfunction() + + +function(c4_install_target_add_files target files destination relative_to) + c4_dbg("installing additional files for target ${target}, destination=${destination}: ${files}") + string(REPLACE ";" "%%%" rfiles "${files}") + c4_append_target_prop(${target} ADDFILES "${rfiles}!!${destination}!!${relative_to}") + # + _c4_is_incorporated(${_c4_prefix} inc) + if(inc) + c4_dbg("this project is INCORPORATEd. skipping install of targets") + return() + endif() + c4_install_files("${files}" "${destination}" "${relative_to}") +endfunction() + + +function(c4_install_target_add_dirs target dirs destination relative_to) + c4_dbg("installing additional dirs for target ${target}, destination=${destination}: ${dirs}") + string(REPLACE ";" "%%%" rdirs "${dirs}") + c4_append_target_prop(${target} ADDDIRS "${rdirs}!!${destination}!!${relative_to}") + # + _c4_is_incorporated(${_c4_prefix} inc) + if(inc) + c4_dbg("this project is INCORPORATEd. skipping install of targets") + return() + endif() + c4_install_dirs("${dirs}" "${destination}" "${relative_to}") +endfunction() + + +function(c4_install_files files destination relative_to) + c4_dbg("adding files to install list, destination ${destination}: ${files}") + foreach(f ${files}) + file(RELATIVE_PATH rf "${relative_to}" ${f}) + get_filename_component(rd "${rf}" DIRECTORY) + install(FILES ${f} DESTINATION "${destination}/${rd}" ${ARGN}) + endforeach() +endfunction() + + +function(c4_install_directories directories destination relative_to) + c4_dbg("adding directories to install list, destination ${destination}: ${directories}") + foreach(d ${directories}) + file(RELATIVE_PATH rf "${relative_to}" ${d}) + get_filename_component(rd "${rf}" DIRECTORY) + install(DIRECTORY ${d} DESTINATION "${destination}/${rd}" ${ARGN}) + endforeach() +endfunction() + + +function(c4_install_exports) + _c4_handle_args(_ARGS ${ARGN} + _ARGS1 # one-value macro arguments + PREFIX # override the c4 project-wide prefix. This will be used in the cmake + TARGET # the name of the exports target + NAMESPACE # the namespace for the targets + _ARGSN # multi-value macro arguments + DEPENDENCIES + ) + # + _c4_handle_arg(PREFIX "${_c4_prefix}") + _c4_handle_arg(TARGET "${_c4_prefix}-export") + _c4_handle_arg(NAMESPACE "${_c4_prefix}::") + # + c4_dbg("installing exports: ${ARGN}") + #_c4_is_incorporated(${_c4_prefix} inc) + #if(inc) + # c4_dbg("this project is INCORPORATEd. skipping install of exports") + # return() + #endif() + # + _c4_setup_install_vars() + # + list(GET ${_c4_prefix}_TARGETS 0 target) + set(exported_target "${_NAMESPACE}${target}") + set(targets_file "${_PREFIX}Targets.cmake") + # + set(deps) + if(_DEPENDENCIES) + set(deps "#----------------------------- +include(CMakeFindDependencyMacro) +") + foreach(d ${_DEPENDENCIES}) + _c4_is_incorporated(${d} inc) + if(inc) + c4_dbg("install: dependency ${d} is INCORPORATEd, skipping check") + continue() + endif() + c4_dbg("install: adding dependency check for ${d}") + set(deps "${deps}find_dependency(${d} REQUIRED) +") + endforeach() + set(deps "${deps}#-----------------------------") + endif() + # + # cfg_dst is the path relative to install root where the export + # should be installed; cfg_dst_rel is the path from there to + # the install root + macro(__c4_install_exports cfg_dst cfg_dst_rel) + # make sure that different exports are staged in different directories + set(case ${CMAKE_CURRENT_BINARY_DIR}/export_cases/${cfg_dst}) + file(MAKE_DIRECTORY ${case}) + # + install(EXPORT "${_TARGET}" + FILE "${targets_file}" + NAMESPACE "${_NAMESPACE}" + DESTINATION "${cfg_dst}") + export(EXPORT ${_TARGET} + FILE "${targets_file}" + NAMESPACE "${_NAMESPACE}") + # + # Config files + # the module below has nice docs in it; do read them + # to understand the macro calls below + include(CMakePackageConfigHelpers) + set(cfg ${case}/${_PREFIX}Config.cmake) + set(cfg_ver ${case}/${_PREFIX}ConfigVersion.cmake) + # + file(WRITE ${cfg}.in "${deps} +set(${_c4_uprefix}VERSION ${${_c4_uprefix}VERSION}) + +@PACKAGE_INIT@ + +if(NOT TARGET ${exported_target}) + include(\${PACKAGE_PREFIX_DIR}/${targets_file}) +endif() + +# HACK: PACKAGE_PREFIX_DIR is obtained from the PACKAGE_INIT macro above; +# When used below in the calls to set_and_check(), +# it points at the location of this file. So point it instead +# to the CMAKE_INSTALL_PREFIX, in relative terms +get_filename_component(PACKAGE_PREFIX_DIR + \"\${PACKAGE_PREFIX_DIR}/${cfg_dst_rel}\" ABSOLUTE) + +set_and_check(${_c4_uprefix}INCLUDE_DIR \"@PACKAGE__INCLUDE_INSTALL_DIR@\") +set_and_check(${_c4_uprefix}LIB_DIR \"@PACKAGE__LIBRARY_INSTALL_DIR@\") +#set_and_check(${_c4_uprefix}SYSCONFIG_DIR \"@PACKAGE__SYSCONFIG_INSTALL_DIR@\") + +check_required_components(${_c4_lcprefix}) +") + configure_package_config_file(${cfg}.in ${cfg} + INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" # defaults to CMAKE_INSTALL_PREFIX + INSTALL_DESTINATION "${CMAKE_INSTALL_PREFIX}" + PATH_VARS + _INCLUDE_INSTALL_DIR + _LIBRARY_INSTALL_DIR + _SYSCONFIG_INSTALL_DIR + #NO_SET_AND_CHECK_MACRO + #NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) + write_basic_package_version_file( + ${cfg_ver} + VERSION ${${_c4_uprefix}VERSION} + COMPATIBILITY AnyNewerVersion + ) + install(FILES ${cfg} ${cfg_ver} DESTINATION ${cfg_dst}) + endmacro(__c4_install_exports) + # + # To install the exports: + # + # Windows: + # <prefix>/ + # <prefix>/(cmake|CMake)/ + # <prefix>/<name>*/ + # <prefix>/<name>*/(cmake|CMake)/ + # + # Unix: + # <prefix>/(lib/<arch>|lib|share)/cmake/<name>*/ + # <prefix>/(lib/<arch>|lib|share)/<name>*/ + # <prefix>/(lib/<arch>|lib|share)/<name>*/(cmake|CMake)/ + # + # Apple: + # <prefix>/<name>.framework/Resources/ + # <prefix>/<name>.framework/Resources/CMake/ + # <prefix>/<name>.framework/Versions/*/Resources/ + # <prefix>/<name>.framework/Versions/*/Resources/CMake/ + # <prefix>/<name>.app/Contents/Resources/ + # <prefix>/<name>.app/Contents/Resources/CMake/ + # + # (This was taken from the find_package() documentation) + if(WIN32) + __c4_install_exports(cmake/ "..") + elseif(APPLE) + __c4_install_exports(${_ARCHIVE_INSTALL_DIR}/cmake/${_c4_prefix} "../../..") + #__c4_install_exports(${_ARCHIVE_INSTALL_DIR}/${_c4_prefix}.framework/Resources/ "../../..") + elseif(UNIX OR (CMAKE_SYSTEM_NAME STREQUAL UNIX) OR (CMAKE_SYSTEM_NAME STREQUAL Linux) OR (CMAKE_SYSTEM_NAME STREQUAL Generic)) + __c4_install_exports(${_ARCHIVE_INSTALL_DIR}/cmake/${_c4_prefix} "../../..") + else() + c4_err("unknown platform. CMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}") + endif() +endfunction() + + +macro(_c4_setup_install_vars) + set(_RUNTIME_INSTALL_DIR bin/) + set(_ARCHIVE_INSTALL_DIR lib/) + set(_LIBRARY_INSTALL_DIR lib/) # TODO on Windows, ARCHIVE and LIBRARY dirs must be different to prevent name clashes + set(_INCLUDE_INSTALL_DIR include/) + set(_OBJECTS_INSTALL_DIR obj/) + set(_SYSCONFIG_INSTALL_DIR etc/${_c4_lcprefix}/) +endmacro() + + +function(c4_get_target_installed_headers target out) + c4_get_target_prop(${target} INCORPORATED_TARGETS inctargets) + if(inctargets) + set(targets "${inctargets};${target}") + else() + set(targets "${target}") + endif() + set(hdrs) + foreach(t ${targets}) + _c4_get_tgt_prop(srcroot ${t} C4_SOURCE_ROOT) + # + c4_get_target_prop(${t} PUBLIC_SRC_${t} src) + if(src) + _c4cat_filter_hdrs("${src}" srcf) + if(thdrs) + set(thdrs "${thdrs};${srcf}") + else() + set(thdrs "${srcf}") + endif() + endif() + # + c4_get_target_prop(${t} PRIVATE_SRC_${t} psrc) + if(src) + _c4cat_filter_hdrs("${psrc}" psrcf) + if(thdrs) + set(thdrs "${thdrs};${psrcf}") + else() + set(thdrs "${psrcf}") + endif() + endif() + # + c4_get_target_prop(${t} INTERFACE_SRC_${t} isrc) + if(src) + _c4cat_filter_hdrs("${isrc}" isrcf) + if(thdrs) + set(thdrs "${thdrs};${isrcf}") + else() + set(thdrs "${isrcf}") + endif() + endif() + # + foreach(h ${thdrs}) + file(RELATIVE_PATH rf "${srcroot}" "${h}") + list(APPEND hdrs "${rf}") + endforeach() + endforeach() + set(${out} ${hdrs} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +function(c4_setup_testing) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 + GTEST # download and import googletest + DOCTEST # download and import doctest + _ARGS1 + _ARGSN + ) + #include(GoogleTest) # this module requires at least cmake 3.9 + c4_dbg("enabling tests") + # umbrella target for building test binaries + add_custom_target(${_c4_lprefix}test-build) + _c4_set_target_folder(${_c4_lprefix}test-build test) + # umbrella targets for running tests + if(NOT TARGET test-build) + add_custom_target(test-build) + add_custom_target(test-verbose) + _c4_set_target_folder(test-build "/test") + _c4_set_target_folder(test-verbose "/test") + endif() + if(NOT TARGET test) + # add a test target. To prevent a warning, we need to set up a policy, + # and also suppress the resulting warning from suppressing the warning. + set(_depr_old_val ${CMAKE_WARN_DEPRECATED}) + set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) # https://stackoverflow.com/questions/67432538/cannot-set-cmake-warn-deprecated-inside-the-cmakelists-txt + cmake_policy(PUSH) + cmake_policy(SET CMP0037 OLD) # target name "test" is reserved for CTesting + add_custom_target(test) + _c4_set_target_folder(test "/test") + cmake_policy(POP) + set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "${_depr_old_val}" FORCE) + unset(_depr_old_val) + endif() + function(_def_runner runner) + set(echo " +CWD=${CMAKE_CURRENT_BINARY_DIR} +---------------------------------- +${ARGN} +---------------------------------- +") + add_custom_target(${runner} + #${CMAKE_COMMAND} -E echo "${echo}" + COMMAND ${ARGN} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${_c4_lprefix}test-build + ) + _c4_set_target_folder(${runner} test) + endfunction() + _def_runner(${_c4_lprefix}test-run ${CMAKE_CTEST_COMMAND} --output-on-failure ${${_c4_uprefix}CTEST_OPTIONS} -C $<CONFIG>) + _def_runner(${_c4_lprefix}test-run-verbose ${CMAKE_CTEST_COMMAND} -VV ${${_c4_uprefix}CTEST_OPTIONS} -C $<CONFIG>) + add_dependencies(test ${_c4_lprefix}test-run) + add_dependencies(test-verbose ${_c4_lprefix}test-run-verbose) + add_dependencies(test-build ${_c4_lprefix}test-build) + # + # import required libraries + if(_GTEST) + c4_log("testing requires googletest") + if(NOT TARGET gtest) + c4_import_remote_proj(gtest ${CMAKE_CURRENT_BINARY_DIR}/ext/gtest + REMOTE + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 #GIT_SHALLOW ON + OVERRIDE + BUILD_GTEST ON + BUILD_GMOCK OFF + gtest_force_shared_crt ON + gtest_build_samples OFF + gtest_build_tests OFF + SET_FOLDER_TARGETS ext gtest gtest_main + EXCLUDE_FROM_ALL + ) + # old gcc-4.8 support + if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND + (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) AND + (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)) + _c4_get_subproject_property(gtest SRC_DIR _gtest_patch_src_dir) + apply_patch("${_c4_project_dir}/compat/gtest_gcc-4.8.patch" + "${_gtest_patch_src_dir}" + "${_gtest_patch_src_dir}/.gtest_gcc-4.8.patch") + unset(_gtest_patch_src_dir) + target_compile_options(gtest PUBLIC -include ${_c4_project_dir}/compat/c4/gcc-4.8.hpp) + endif() + endif() + endif() + if(_DOCTEST) + c4_log("testing requires doctest") + if(NOT TARGET doctest) + c4_import_remote_proj(doctest ${CMAKE_CURRENT_BINARY_DIR}/ext/doctest + REMOTE + GIT_REPOSITORY https://github.com/onqtam/doctest.git + GIT_TAG 2.4.6 #GIT_SHALLOW ON + OVERRIDE + DOCTEST_WITH_TESTS OFF + DOCTEST_WITH_MAIN_IN_STATIC_LIB ON + SET_FOLDER_TARGETS ext doctest_with_main + EXCLUDE_FROM_ALL + ) + endif() + endif() +endfunction(c4_setup_testing) + + +function(c4_add_test target) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + _ARGS1 # one-value macro arguments + WORKING_DIRECTORY + _ARGSN # multi-value macro arguments + ARGS + ) + # + if(_WORKING_DIRECTORY) + set(_WORKING_DIRECTORY WORKING_DIRECTORY ${_WORKING_DIRECTORY}) + endif() + set(cmd_pfx) + if(CMAKE_CROSSCOMPILING) + set(cmd_pfx ${CMAKE_CROSSCOMPILING_EMULATOR}) + endif() + if(NOT ${uprefix}SANITIZE_ONLY) + if(${CMAKE_VERSION} VERSION_LESS "3.16.0") + add_test(NAME ${target} + COMMAND ${cmd_pfx} "$<TARGET_FILE:${target}>" ${_ARGS} + ${_WORKING_DIRECTORY}) + else() + add_test(NAME ${target} + COMMAND ${cmd_pfx} "$<TARGET_FILE:${target}>" ${_ARGS} + ${_WORKING_DIRECTORY} + COMMAND_EXPAND_LISTS) + endif() + endif() + # + if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") + add_dependencies(${_c4_lprefix}test-build ${target}) + return() + endif() + # + set(sanitized_targets) + foreach(s asan msan tsan ubsan) + set(t ${target}-${s}) + if(TARGET ${t}) + list(APPEND sanitized_targets ${s}) + endif() + endforeach() + if(sanitized_targets) + add_custom_target(${target}-all) + add_dependencies(${target}-all ${target}) + add_dependencies(${_c4_lprefix}test-build ${target}-all) + _c4_set_target_folder(${target}-all test/${target}) + else() + add_dependencies(${_c4_lprefix}test-build ${target}) + endif() + if(sanitized_targets) + foreach(s asan msan tsan ubsan) + set(t ${target}-${s}) + if(TARGET ${t}) + add_dependencies(${target}-all ${t}) + c4_sanitize_get_target_command("${cmd_pfx};$<TARGET_FILE:${t}>" ${s} cmd) + #c4_log("adding test: ${t}") + add_test(NAME ${t} + COMMAND ${cmd} ${_ARGS} + ${_WORKING_DIRECTORY} + COMMAND_EXPAND_LISTS) + endif() + endforeach() + endif() + if(NOT CMAKE_CROSSCOMPILING) + if(NOT ${_c4_uprefix}SANITIZE_ONLY) + c4_add_valgrind(${target} ${ARGN}) + endif() + endif() + if(${_c4_uprefix}LINT) + c4_static_analysis_add_tests(${target}) # this will not actually run the executable + endif() +endfunction(c4_add_test) + + +# every excess argument is passed on to set_target_properties() +function(c4_add_test_fail_build name srccontent_or_srcfilename) + # + set(sdir ${CMAKE_CURRENT_BINARY_DIR}/test_fail_build) + set(src ${srccontent_or_srcfilename}) + if("${src}" STREQUAL "") + c4_err("must be given an existing source file name or a non-empty string") + endif() + # + if(EXISTS ${src}) + set(fn ${src}) + else() + if(NOT EXISTS ${sdir}) + file(MAKE_DIRECTORY ${sdir}) + endif() + set(fn ${sdir}/${name}.cpp) + file(WRITE ${fn} "${src}") + endif() + # + # https://stackoverflow.com/questions/30155619/expected-build-failure-tests-in-cmake + add_executable(${name} ${fn}) + # don't build this target + set_target_properties(${name} PROPERTIES + EXCLUDE_FROM_ALL TRUE + EXCLUDE_FROM_DEFAULT_BUILD TRUE + # and pass on further properties given by the caller + ${ARGN}) + add_test(NAME ${name} + COMMAND ${CMAKE_COMMAND} --build . --target ${name} --config $<CONFIGURATION> + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + set_tests_properties(${name} PROPERTIES WILL_FAIL TRUE) +endfunction() + + +# add a test ensuring that a target linking and using code from a library +# successfully compiles and runs against the installed library +function(c4_add_install_link_test library namespace exe_source_code) + if(CMAKE_CROSSCOMPILING) + c4_log("cross-compiling: skip install link test") + return() + endif() + if("${library}" STREQUAL "${_c4_prefix}") + set(testname ${_c4_lprefix}test-install-link) + else() + set(testname ${_c4_lprefix}test-install-link-${library}) + endif() + _c4_add_library_client_test(${library} "${namespace}" "${testname}" "${exe_source_code}") +endfunction() + + +# add a test ensuring that a target consuming every header in a library +# successfully compiles and runs against the installed library +function(c4_add_install_include_test library namespace) + if(CMAKE_CROSSCOMPILING) + c4_log("cross-compiling: skip install include test") + return() + endif() + c4_get_target_installed_headers(${library} incfiles) + set(incblock) + foreach(i ${incfiles}) + set(incblock "${incblock} +#include <${i}>") + endforeach() + set(src "${incblock} + +int main() +{ + return 0; +} +") + if("${library}" STREQUAL "${_c4_prefix}") + set(testname ${_c4_lprefix}test-install-include) + else() + set(testname ${_c4_lprefix}test-install-include-${library}) + endif() + _c4_add_library_client_test(${library} "${namespace}" "${testname}" "${src}") +endfunction() + + +function(_c4_add_library_client_test library namespace pname source_code) + if("${CMAKE_BUILD_TYPE}" STREQUAL Coverage) + add_test(NAME ${pname} + COMMAND ${CMAKE_COMMAND} -E echo "skipping this test in coverage builds" + ) + return() + endif() + set(pdir "${CMAKE_CURRENT_BINARY_DIR}/${pname}") + set(bdir "${pdir}/build") + if(NOT EXISTS "${pdir}") + file(MAKE_DIRECTORY "${pdir}") + endif() + if(NOT EXISTS "${bdir}/build") + file(MAKE_DIRECTORY "${bdir}/build") + endif() + set(psrc "${pdir}/${pname}.cpp") + set(tsrc "${pdir}/${pname}-run.cmake") + set(tout "${pdir}/${pname}-run-out") + # generate the source file + file(WRITE "${psrc}" "${source_code}") + # generate the cmake project consuming this library + file(WRITE "${pdir}/CMakeLists.txt" " +cmake_minimum_required(VERSION 3.12) +project(${pname} LANGUAGES CXX) + +find_package(${library} REQUIRED) + +message(STATUS \" +found ${library}: + ${_c4_uprefix}INCLUDE_DIR=\${${_c4_uprefix}INCLUDE_DIR} + ${_c4_uprefix}LIB_DIR=\${${_c4_uprefix}LIB_DIR} +\") + +add_executable(${pname} ${pname}.cpp) +# this must be the only required setup to link with ${library} +target_link_libraries(${pname} PUBLIC ${namespace}${library}) + +get_target_property(lib_type ${namespace}${library} TYPE) +if(WIN32 AND (lib_type STREQUAL SHARED_LIBRARY)) + # add the directory containing the DLL to the path + get_target_property(imported_configs ${namespace}${library} IMPORTED_CONFIGURATIONS) + message(STATUS \"${namespace}${library}: it's a shared library. imported configs: \${imported_configs}\") + foreach(cfg \${imported_configs}) + get_target_property(implib ${namespace}${library} IMPORTED_IMPLIB_\${cfg}) + get_target_property(location ${namespace}${library} IMPORTED_LOCATION_\${cfg}) + message(STATUS \"${namespace}${library}: implib_\${cfg}=\${implib}\") + message(STATUS \"${namespace}${library}: location_\${cfg}=\${location}\") + break() + endforeach() + get_filename_component(dlldir \"\${location}\" DIRECTORY) + message(STATUS \"${namespace}${library}: dlldir=\${dlldir}\") + add_custom_target(${pname}-run + COMMAND \${CMAKE_COMMAND} -E echo \"cd \${dlldir} && \$<TARGET_FILE:${pname}>\" + COMMAND \$<TARGET_FILE:${pname}> + DEPENDS ${pname} + WORKING_DIRECTORY \${dlldir}) +else() + add_custom_target(${pname}-run + COMMAND \$<TARGET_FILE:${pname}> + DEPENDS ${pname}) +endif() +") + # The test consists in running the script generated below. + # We force evaluation of the configuration generator expression + # by receiving its result via the command line. + add_test(NAME ${pname} + COMMAND ${CMAKE_COMMAND} -DCFG_IN=$<CONFIG> -P "${tsrc}" + ) + # NOTE: in the cmake configure command, be sure to NOT use quotes + # in -DCMAKE_PREFIX_PATH=\"${CMAKE_INSTALL_PREFIX}\". Use + # -DCMAKE_PREFIX_PATH=${CMAKE_INSTALL_PREFIX} instead. + # So here we add a check to make sure the install path has no spaces + string(FIND "${CMAKE_INSTALL_PREFIX}" " " has_spaces) + if(NOT (has_spaces EQUAL -1)) + c4_err("install tests will fail if the install path has spaces: '${CMAKE_INSTALL_PREFIX}' : ... ${has_spaces}") + endif() + # make sure the test project uses the same architecture + # CMAKE_VS_PLATFORM_NAME is available only since cmake 3.9 + # see https://cmake.org/cmake/help/v3.9/variable/CMAKE_GENERATOR_PLATFORM.html + if(WIN32) + set(cfg_opt "--config \${cfg}") + if(CMAKE_GENERATOR_PLATFORM OR CMAKE_VS_PLATFORM_NAME) + set(arch "-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}" "-DCMAKE_VS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME}") + else() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(arch -A x64) + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(arch -A Win32) + else() + c4_err("not implemented") + endif() + endif() + elseif(ANDROID OR IOS OR WINCE OR WINDOWS_PHONE) + c4_err("not implemented") + elseif(IOS) + c4_err("not implemented") + elseif(UNIX) + if(CMAKE_GENERATOR_PLATFORM OR CMAKE_VS_PLATFORM_NAME) + set(arch "-DCMAKE_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}" "-DCMAKE_VS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME}") + else() + if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64) + else() + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(arch "-DCMAKE_CXX_FLAGS=-m64") + elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(arch "-DCMAKE_CXX_FLAGS=-m32") + else() + c4_err("not implemented") + endif() + endif() + endif() + endif() + # generate the cmake script with the test content + file(WRITE "${tsrc}" " +# run a command and check its return status +function(runcmd id) + set(cmdout \"${tout}-\${id}.log\") + message(STATUS \"Running command: \${ARGN}\") + message(STATUS \"Running command: output goes to \${cmdout}\") + execute_process( + COMMAND \${ARGN} + RESULT_VARIABLE retval + OUTPUT_FILE \"\${cmdout}\" + ERROR_FILE \"\${cmdout}\" + # COMMAND_ECHO STDOUT # only available from cmake-3.15 + ) + message(STATUS \"Running command: exit status was \${retval}\") + file(READ \"\${cmdout}\" output) + if(\"\${cmdout}\" STREQUAL \"\") + message(STATUS \"Running command: no output\") + else() + message(STATUS \"Running command: output: +-------------------- +\${output}--------------------\") + endif() + if(NOT (\${retval} EQUAL 0)) + message(FATAL_ERROR \"Command failed with exit status \${retval}: \${ARGN}\") + endif() +endfunction() + +set(cmk \"${CMAKE_COMMAND}\") +set(pfx \"${CMAKE_INSTALL_PREFIX}\") +set(idir \"${CMAKE_BINARY_DIR}\") +set(pdir \"${pdir}\") +set(bdir \"${bdir}\") + +# force evaluation of the configuration generator expression +# by receiving its result via the command line +set(cfg \${CFG_IN}) + +# remove any existing library install +if(EXISTS \"\${pfx}\") + runcmd(0_rmdir \"\${cmk}\" -E remove_directory \"\${pfx}\") +else() + message(STATUS \"does not exist: \${pfx}\") +endif() + +# install the library +#runcmd(1_install_lib \"\${cmk}\" --install \"\${idir}\" ${cfg_opt}) # requires cmake>3.13 (at least) +runcmd(1_install_lib \"\${cmk}\" --build \"\${idir}\" ${cfg_opt} --target install) + +# configure the client project +runcmd(2_config \"\${cmk}\" -S \"\${pdir}\" -B \"\${bdir}\" \"-DCMAKE_PREFIX_PATH=\${pfx}\" \"-DCMAKE_GENERATOR=${CMAKE_GENERATOR}\" ${arch} \"-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}\" \"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}\") + +# build the client project +runcmd(3_build \"\${cmk}\" --build \"\${bdir}\" ${cfg_opt}) + +# run the client executable +runcmd(4_install \"\${cmk}\" --build \"\${bdir}\" --target \"${pname}-run\" ${cfg_opt}) +") +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +function(c4_setup_valgrind umbrella_option) + if(UNIX AND (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")) + if("${C4_VALGRIND}" STREQUAL "") + option(C4_VALGRIND "enable valgrind tests (all subprojects)" ON) + endif() + if("${C4_VALGRIND_SGCHECK}" STREQUAL "") + option(C4_VALGRIND_SGCHECK "enable valgrind tests with the exp-sgcheck tool (all subprojects)" OFF) + endif() + cmake_dependent_option(${_c4_uprefix}VALGRIND "enable valgrind tests" ${C4_VALGRIND} ${umbrella_option} OFF) + cmake_dependent_option(${_c4_uprefix}VALGRIND_SGCHECK "enable valgrind tests with the exp-sgcheck tool" ${C4_VALGRIND_SGCHECK} ${umbrella_option} OFF) + set(${_c4_uprefix}VALGRIND_OPTIONS "--gen-suppressions=all --error-exitcode=10101" CACHE STRING "options for valgrind tests") + endif() +endfunction(c4_setup_valgrind) + + +function(c4_add_valgrind target_name) + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + _ARGS1 # one-value macro arguments + WORKING_DIRECTORY + _ARGSN # multi-value macro arguments + ARGS + ) + # + if(_WORKING_DIRECTORY) + set(_WORKING_DIRECTORY WORKING_DIRECTORY ${_WORKING_DIRECTORY}) + endif() + # @todo: consider doing this for valgrind: + # http://stackoverflow.com/questions/40325957/how-do-i-add-valgrind-tests-to-my-cmake-test-target + # for now we explicitly run it: + if(${_c4_uprefix}VALGRIND) + separate_arguments(_vg_opts UNIX_COMMAND "${${_c4_uprefix}VALGRIND_OPTIONS}") + add_test(NAME ${target_name}-valgrind + COMMAND valgrind ${_vg_opts} $<TARGET_FILE:${target_name}> ${_ARGS} + ${_WORKING_DIRECTORY} + COMMAND_EXPAND_LISTS) + endif() + if(${_c4_uprefix}VALGRIND_SGCHECK) + # stack and global array overrun detector + # http://valgrind.org/docs/manual/sg-manual.html + separate_arguments(_sg_opts UNIX_COMMAND "--tool=exp-sgcheck ${${_c4_uprefix}VALGRIND_OPTIONS}") + add_test(NAME ${target_name}-sgcheck + COMMAND valgrind ${_sg_opts} $<TARGET_FILE:${target_name}> ${_ARGS} + ${_WORKING_DIRECTORY} + COMMAND_EXPAND_LISTS) + endif() +endfunction(c4_add_valgrind) + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_setup_coverage) + if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage")) + return() + endif() + # + _c4_handle_args(_ARGS ${ARGN} + _ARGS0 # zero-value macro arguments + _ARGS1 # one-value macro arguments + _ARGSN # multi-value macro arguments + COVFLAGS # coverage compilation flags + INCLUDE # patterns to include in the coverage, relative to CMAKE_SOURCE_DIR + EXCLUDE # patterns to exclude in the coverage, relative to CMAKE_SOURCE_DIR + EXCLUDE_ABS # absolute paths to exclude in the coverage + GENHTML_ARGS # options to pass to genhtml + ) + # defaults for the macro arguments + set(_genhtml_args "--title ${_c4_lcprefix} --demangle-cpp --sort --function-coverage --branch-coverage --prefix '${CMAKE_SOURCE_DIR}' --prefix '${CMAKE_BINARY_DIR}'") + set(covflags "-g -O0 --coverage") #set(covflags "-g -O0 -fprofile-arcs -ftest-coverage") + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(covflags "${covflags} -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline") + endif() + set(${_c4_uprefix}COVERAGE_FLAGS "${covflags}" CACHE STRING "coverage compilation flags") + set(${_c4_uprefix}COVERAGE_GENHTML_ARGS "${_genhtml_args}" CACHE STRING "arguments to pass to genhtml") + set(${_c4_uprefix}COVERAGE_INCLUDE src CACHE STRING "relative paths to include in the coverage, relative to CMAKE_SOURCE_DIR") + set(${_c4_uprefix}COVERAGE_EXCLUDE bm;build;extern;ext;src/c4/ext;test CACHE STRING "relative paths to exclude from the coverage, relative to CMAKE_SOURCE_DIR") + set(${_c4_uprefix}COVERAGE_EXCLUDE_ABS /usr CACHE STRING "absolute paths to exclude from the coverage") + _c4_handle_arg(COVFLAGS ${${_c4_uprefix}COVERAGE_FLAGS}) + _c4_handle_arg(INCLUDE ${${_c4_uprefix}COVERAGE_INCLUDE}) + _c4_handle_arg(EXCLUDE ${${_c4_uprefix}COVERAGE_EXCLUDE}) + _c4_handle_arg(EXCLUDE_ABS ${${_c4_uprefix}COVERAGE_EXCLUDE_ABS} "${CMAKE_BINARY_DIR}") + _c4_handle_arg(GENHTML_ARGS ${${_c4_uprefix}COVERAGE_GENHTML_ARGS}) + # + function(_c4cov_transform_filters var reldir) + set(_filters) + foreach(pat ${${var}}) + list(APPEND _filters "'${reldir}${pat}/*'") + endforeach() + set(${var} ${_filters} PARENT_SCOPE) + endfunction() + _c4cov_transform_filters(_INCLUDE "${CMAKE_SOURCE_DIR}/") + _c4cov_transform_filters(_EXCLUDE "${CMAKE_SOURCE_DIR}/") + _c4cov_transform_filters(_EXCLUDE_ABS "") + # + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3) + c4_err("coverage: clang version must be 3.0.0 or greater") + endif() + elseif(NOT CMAKE_COMPILER_IS_GNUCXX) + c4_err("coverage: compiler is not GNUCXX") + endif() + # + find_program(GCOV gcov) + find_program(LCOV lcov) + find_program(GENHTML genhtml) + find_program(CTEST ctest) + if(NOT (GCOV AND LCOV AND GENHTML AND CTEST)) + c4_err("Coverage tools not available: + gcov: ${GCOV} + lcov: ${LCOV} + genhtml: ${GENHTML} + ctest: ${CTEST} + --coverage flags: ${_COVFLAGS}") + endif() + # + add_configuration_type(Coverage + DEFAULT_FROM DEBUG + C_FLAGS ${_COVFLAGS} + CXX_FLAGS ${_COVFLAGS} + ) + # + option(${_c4_uprefix}COVERAGE_CODECOV "enable target to submit coverage to codecov.io" OFF) + option(${_c4_uprefix}COVERAGE_COVERALLS "enable target to submit coverage to coveralls.io" OFF) + # + c4_dbg("adding coverage targets") + # + set(sd "${CMAKE_SOURCE_DIR}") + set(bd "${CMAKE_BINARY_DIR}") + set(coverage_result ${bd}/lcov/index.html) + set(lcov_result ${bd}/coverage3-final_filtered.lcov) + separate_arguments(_GENHTML_ARGS NATIVE_COMMAND ${_GENHTML_ARGS}) + add_custom_command(OUTPUT ${coverage_result} ${lcov_result} + COMMAND echo "cd ${CMAKE_BINARY_DIR}" + COMMAND ${LCOV} -q --zerocounters --directory . + COMMAND ${LCOV} -q --no-external --capture --base-directory "${sd}" --directory . --output-file ${bd}/coverage0-before.lcov --initial + COMMAND ${CMAKE_COMMAND} --build . --target ${_c4_lprefix}test-run || echo "Failed running the tests. Proceeding with coverage, but results may be affected or even empty." + COMMAND ${LCOV} -q --no-external --capture --base-directory "${sd}" --directory . --output-file ${bd}/coverage1-after.lcov + COMMAND ${LCOV} -q --add-tracefile ${bd}/coverage0-before.lcov --add-tracefile ${bd}/coverage1-after.lcov --output-file ${bd}/coverage2-final.lcov + COMMAND ${LCOV} -q --remove ${bd}/coverage2-final.lcov ${_EXCLUDE} ${EXCLUDE_ABS} --output-file ${bd}/coverage3-final_filtered.lcov + COMMAND ${GENHTML} ${bd}/coverage3-final_filtered.lcov -o ${bd}/lcov ${_GENHTML_ARGS} + COMMAND echo "Coverage report: ${coverage_result}" + DEPENDS ${_c4_lprefix}test-build + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "${_c4_prefix} coverage: LCOV report at ${coverage_result}" + #VERBATIM + ) + add_custom_target(${_c4_lprefix}coverage SOURCES ${coverage_result} ${lcov_result}) + # + if(${_c4_uprefix}COVERAGE_CODECOV) + set(_subm ${_c4_lprefix}coverage-submit-codecov) + _c4cov_get_service_token(codecov _token) + if(NOT ("${_token}" STREQUAL "")) + set(_token -t "${_token}") + endif() + set(_silent_codecov) + if(${_c4_uprefix}COVERAGE_CODECOV_SILENT) + set(_silent_codecov >${CMAKE_BINARY_DIR}/codecov.log 2>&1) + endif() + # + c4_log("coverage: enabling submission of results to https://codecov.io: ${_subm}") + set(submitcc "${CMAKE_BINARY_DIR}/submit_codecov.sh") + c4_download_file("https://codecov.io/bash" "${submitcc}") + set(submit_cmd bash ${submitcc} -Z ${_token} -X gcov -X gcovout -p ${CMAKE_SOURCE_DIR} -f ${lcov_result} ${_silent_codecov}) + string(REPLACE ";" " " submit_cmd_str "${submit_cmd}") + add_custom_target(${_subm} + SOURCES ${lcov_result} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND echo "cd ${CMAKE_BINARY_DIR} && ${submit_cmd_str}" + COMMAND ${submit_cmd} + VERBATIM + COMMENT "${_c4_lcprefix} coverage: submit to codecov" + ) + c4_add_umbrella_target(coverage-submit-codecov coverage-submit) # uses the current prefix + endif() + # + if(${_c4_uprefix}COVERAGE_COVERALLS) + set(_subm ${_c4_lprefix}coverage-submit-coveralls) + _c4cov_get_service_token(coveralls _token) + if(NOT ("${_token}" STREQUAL "")) + set(_token --repo-token "${_token}") + endif() + set(_silent_coveralls) + if(${_c4_uprefix}COVERAGE_COVERALLS_SILENT) + set(_silent_coveralls >${CMAKE_BINARY_DIR}/coveralls.log 2>&1) + endif() + # + c4_log("coverage: enabling submission of results to https://coveralls.io: ${_subm}") + set(submit_cmd coveralls ${_token} --build-root ${CMAKE_BINARY_DIR} --root ${CMAKE_SOURCE_DIR} --no-gcov --lcov-file ${lcov_result} ${_silent_coveralls}) + string(REPLACE ";" " " submit_cmd_str "${submit_cmd}") + add_custom_target(${_subm} + SOURCES ${lcov_result} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND echo "cd ${CMAKE_BINARY_DIR} && ${submit_cmd_str}" + COMMAND ${submit_cmd} + VERBATIM + COMMENT "${_c4_lcprefix} coverage: submit to coveralls" + ) + c4_add_umbrella_target(coverage-submit-coveralls coverage-submit) # uses the current prefix + endif() +endfunction(c4_setup_coverage) + + +# 1. try cmake or environment variables +# 2. try local file +function(_c4cov_get_service_token service out) + # try cmake var + string(TOUPPER ${service} uservice) + c4_get_from_first_of(token COVERAGE_${uservice}_TOKEN ENV) + if(NOT ("${token}" STREQUAL "")) + c4_dbg("${service}: found token from variable: ${token}") + else() + # try local file + set(service_token_file ${CMAKE_SOURCE_DIR}/.ci/${service}.token) + if(EXISTS ${service_token_file}) + file(READ ${service_token_file} token) + c4_dbg("found token file for ${service} coverage report: ${service_token_file}") + else() + c4_dbg("could not find token for ${service} coverage report") + endif() + endif() + set(${out} ${token} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(c4_add_umbrella_target target umbrella_target) + _c4_handle_args(_ARGS ${ARGN} + # zero-value macro arguments + _ARGS0 + ALWAYS # Add the umbrella target even if this is the only one under it. + # The default behavior is to add the umbrella target only if + # there is more than one target under it. + # one-value macro arguments + _ARGS1 + PREFIX # The project prefix. Defaults to ${_c4_lprefix} + # multi-value macro arguments + _ARGSN + ARGS # more args to add_custom_target() + ) + if(NOT _PREFIX) + set(_PREFIX "${_c4_lprefix}") + endif() + set(t ${_PREFIX}${target}) + set(ut ${_PREFIX}${umbrella_target}) + # if the umbrella target already exists, just add the dependency + if(TARGET ${ut}) + add_dependencies(${ut} ${t}) + else() + if(_ALWAYS) + add_custom_target(${ut} ${_ARGS}) + add_dependencies(${ut} ${t}) + else() + # check if there is more than one under the same umbrella + c4_get_proj_prop(${ut}_subtargets sub) + if(sub) + add_custom_target(${ut} ${_ARGS}) + add_dependencies(${ut} ${sub}) + add_dependencies(${ut} ${t}) + else() + c4_set_proj_prop(${ut}_subtargets ${t}) + endif() + endif() + endif() +endfunction() + + + +function(c4_download_file url dstpath) + c4_dbg("downloading file: ${url} ---> ${dstpath}") + get_filename_component(abspath ${dstpath} ABSOLUTE) + if(NOT EXISTS ${abspath}) + c4_dbg("downloading file: does not exist: ${dstpath}") + file(DOWNLOAD ${url} ${abspath} LOG dl_log STATUS status ${ARGN}) + if((NOT (status EQUAL 0)) OR (NOT EXISTS ${abspath})) + c4_err("error downloading file: ${url} -> ${abspath}:\n${dl_log}") + endif() + endif() +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +function(c4_setup_benchmarking) + c4_log("enabling benchmarks: to build, ${_c4_lprefix}bm-build") + c4_log("enabling benchmarks: to run, ${_c4_lprefix}bm-run") + # umbrella target for building test binaries + add_custom_target(${_c4_lprefix}bm-build) + # umbrella target for running benchmarks + add_custom_target(${_c4_lprefix}bm-run + ${CMAKE_COMMAND} -E echo CWD=${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${_c4_lprefix}bm-build + ) + if(NOT TARGET bm-run) + add_custom_target(bm-run) + add_custom_target(bm-build) + endif() + add_dependencies(bm-run ${_c4_lprefix}bm-run) + add_dependencies(bm-build ${_c4_lprefix}bm-build) + _c4_set_target_folder(${_c4_lprefix}bm-run bm) + _c4_set_target_folder(${_c4_lprefix}bm-build bm) + _c4_set_target_folder(bm-build "/bm") + _c4_set_target_folder(bm-run "/bm") + # download google benchmark + if(NOT TARGET benchmark) + c4_import_remote_proj(googlebenchmark ${CMAKE_CURRENT_BINARY_DIR}/ext/googlebenchmark + REMOTE + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG main GIT_SHALLOW ON + OVERRIDE + BENCHMARK_ENABLE_TESTING OFF + BENCHMARK_ENABLE_EXCEPTIONS OFF + BENCHMARK_ENABLE_LTO OFF + SET_FOLDER_TARGETS ext benchmark benchmark_main + EXCLUDE_FROM_ALL + ) + # + if((CMAKE_CXX_COMPILER_ID STREQUAL GNU) OR (CMAKE_COMPILER_IS_GNUCC)) + target_compile_options(benchmark PRIVATE -Wno-deprecated-declarations) + target_compile_options(benchmark PRIVATE -Wno-restrict) + endif() + # + if(NOT WIN32) + option(${_c4_uprefix}BENCHMARK_CPUPOWER + "set the cpu mode to performance before / powersave after the benchmark" OFF) + if(${_c4_uprefix}BENCHMARK_CPUPOWER) + find_program(C4_SUDO sudo) + find_program(C4_CPUPOWER cpupower) + endif() + endif() + endif() +endfunction() + + +function(c4_add_benchmark_cmd casename) + add_custom_target(${casename} + COMMAND ${ARGN} + VERBATIM + COMMENT "${_c4_prefix}: running benchmark ${casename}: ${ARGN}") + add_dependencies(${_c4_lprefix}bm-build ${casename}) + _c4_set_target_folder(${casename} bm) +endfunction() + + +# assumes this is a googlebenchmark target, and that multiple +# benchmarks are defined from it +function(c4_add_target_benchmark target casename) + set(opt0arg + ) + set(opt1arg + WORKDIR # working directory + FILTER # benchmark patterns to filter + UMBRELLA_TARGET + RESULTS_FILE + ) + set(optnarg + ARGS + ) + cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN}) + # + set(name "${target}-${casename}") + set(rdir "${CMAKE_CURRENT_BINARY_DIR}/bm-results") + set(rfile "${rdir}/${name}.json") + if(_RESULTS_FILE) + set(${_RESULTS_FILE} "${rfile}" PARENT_SCOPE) + endif() + if(NOT EXISTS "${rdir}") + file(MAKE_DIRECTORY "${rdir}") + endif() + set(filter) + if(NOT ("${_FILTER}" STREQUAL "")) + set(filter "--benchmark_filter=${_FILTER}") + endif() + set(args_fwd ${filter} --benchmark_out_format=json --benchmark_out=${rfile} ${_ARGS}) + c4_add_benchmark(${target} + "${name}" + "${_WORKDIR}" + "saving results in ${rfile}" + ${args_fwd} + OUTPUT_FILE ${rfile}) + if(_UMBRELLA_TARGET) + add_dependencies(${_UMBRELLA_TARGET} "${name}") + endif() +endfunction() + + +function(c4_add_benchmark target casename work_dir comment) + set(opt0arg + ) + set(opt1arg + OUTPUT_FILE + ) + set(optnarg + ) + cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN}) + if(NOT TARGET ${target}) + c4_err("target ${target} does not exist...") + endif() + if(NOT ("${work_dir}" STREQUAL "")) + if(NOT EXISTS "${work_dir}") + file(MAKE_DIRECTORY "${work_dir}") + endif() + endif() + set(exe $<TARGET_FILE:${target}>) + if(${_c4_uprefix}BENCHMARK_CPUPOWER) + if(C4_BM_SUDO AND C4_BM_CPUPOWER) + set(c ${C4_SUDO} ${C4_CPUPOWER} frequency-set --governor performance) + set(cpupow_before + COMMAND echo ${c} + COMMAND ${c}) + set(c ${C4_SUDO} ${C4_CPUPOWER} frequency-set --governor powersave) + set(cpupow_after + COMMAND echo ${c} + COMMAND ${c}) + endif() + endif() + if(_OUTPUT_FILE) + set(_OUTPUT_FILE BYPRODUCTS ${_OUTPUT_FILE}) + set(_OUTPUT_FILE) # otherwise the benchmarks run everytime when building depending targets + endif() + add_custom_target(${casename} + ${cpupow_before} + # this is useful to show the target file (you cannot echo generator variables) + #COMMAND ${CMAKE_COMMAND} -E echo "target file = $<TARGET_FILE:${target}>" + COMMAND ${CMAKE_COMMAND} -E echo "${exe} ${ARGN}" + COMMAND "${exe}" ${ARGN} + ${cpupow_after} + VERBATIM + ${_OUTPUT_FILE} + WORKING_DIRECTORY "${work_dir}" + DEPENDS ${target} + COMMENT "${_c4_lcprefix}: running benchmark ${target}, case ${casename}: ${comment}" + ) + add_dependencies(${_c4_lprefix}bm-build ${target}) + add_dependencies(${_c4_lprefix}bm-run ${casename}) + _c4_set_target_folder(${casename} bm/run) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +function(_c4cat_get_outname target id ext out) + if("${_c4_lcprefix}" STREQUAL "${target}") + set(p "${target}") + else() + set(p "${_c4_lcprefix}.${target}") + endif() + set(${out} "${CMAKE_CURRENT_BINARY_DIR}/${p}.${id}.${ext}" PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_srcs in out) + _c4cat_filter_extensions("${in}" "${C4_SRC_EXTS}" l) + set(${out} ${l} PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_hdrs in out) + _c4cat_filter_extensions("${in}" "${C4_HDR_EXTS}" l) + set(${out} ${l} PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_srcs_hdrs in out) + _c4cat_filter_extensions("${in}" "${C4_HDR_EXTS};${C4_SRC_EXTS}" l) + set(${out} ${l} PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_additional_exts in out) + _c4cat_filter_extensions("${in}" "${C4_ADD_EXTS}" l) + set(${out} ${l} PARENT_SCOPE) +endfunction() + +function(_c4cat_filter_extensions in filter out) + set(l) + foreach(fn ${in}) # don't quote the list here + _c4cat_get_file_ext("${fn}" ext) + _c4cat_one_of("${ext}" "${filter}" yes) + if(${yes}) + list(APPEND l "${fn}") + endif() + endforeach() + set(${out} "${l}" PARENT_SCOPE) +endfunction() + +function(_c4cat_get_file_ext in out) + # https://stackoverflow.com/questions/30049180/strip-filename-shortest-extension-by-cmake-get-filename-removing-the-last-ext + string(REGEX MATCH "^.*\\.([^.]*)$" dummy ${in}) + set(${out} ${CMAKE_MATCH_1} PARENT_SCOPE) +endfunction() + +function(_c4cat_one_of ext candidates out) + foreach(e ${candidates}) + if("${ext}" STREQUAL "${e}") + set(${out} TRUE PARENT_SCOPE) + return() + endif() + endforeach() + set(${out} FALSE PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ + +# given a list of source files, return a list with full paths +function(c4_to_full_path source_list source_list_with_full_paths) + set(l) + foreach(f ${source_list}) + if(IS_ABSOLUTE "${f}") + list(APPEND l "${f}") + else() + list(APPEND l "${CMAKE_CURRENT_SOURCE_DIR}/${f}") + endif() + endforeach() + set(${source_list_with_full_paths} ${l} PARENT_SCOPE) +endfunction() + + +# convert a list to a string separated with spaces +function(c4_separate_list input_list output_string) + set(s) + foreach(e ${input_list}) + set(s "${s} ${e}") + endforeach() + set(${output_string} ${s} PARENT_SCOPE) +endfunction() + + + +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +#------------------------------------------------------------------------------ +endif(NOT _c4_project_included) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4SanitizeTarget.cmake b/thirdparty/ryml/ext/c4core/cmake/c4SanitizeTarget.cmake new file mode 100644 index 000000000..a064f91ee --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4SanitizeTarget.cmake @@ -0,0 +1,292 @@ +# (C) 2017 Joao Paulo Magalhaes <[email protected]> +if(NOT _c4_sanitize_target_included) +set(_c4_sanitize_target_included ON) + +include(CMakeDependentOption) +include(PrintVar) + +function(_c4_default_if_not_set var dft) + if("${${var}}" STREQUAL "") + option(${var} "" ${dft}) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_setup_sanitize umbrella_option) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") + return() + endif() + if(NOT ((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") OR (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))) + return() + endif() + + _c4_default_if_not_set(C4_SANITIZE ON) + _c4_default_if_not_set(C4_SANITIZE_ONLY OFF) + _c4_default_if_not_set(C4_ASAN ON) + _c4_default_if_not_set(C4_TSAN ON) + _c4_default_if_not_set(C4_MSAN ON) + _c4_default_if_not_set(C4_UBSAN ON) + + cmake_dependent_option(${_c4_uprefix}SANITIZE "turn on clang sanitizer targets" ${C4_SANITIZE} ${umbrella_option} OFF) + cmake_dependent_option(${_c4_uprefix}SANITIZE_ONLY "compile only sanitize targets (not the regular unsanitized targets)" ${C4_SANITIZE_ONLY} ${umbrella_option} OFF) + + # options for individual sanitizers - contingent on sanitize on/off + cmake_dependent_option(${_c4_uprefix}ASAN "" ${C4_ASAN} "${_c4_uprefix}SANITIZE" OFF) + cmake_dependent_option(${_c4_uprefix}TSAN "" ${C4_TSAN} "${_c4_uprefix}SANITIZE" OFF) + cmake_dependent_option(${_c4_uprefix}MSAN "" ${C4_MSAN} "${_c4_uprefix}SANITIZE" OFF) + cmake_dependent_option(${_c4_uprefix}UBSAN "" ${C4_UBSAN} "${_c4_uprefix}SANITIZE" OFF) + + if(${_c4_uprefix}SANITIZE) + string(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" LLVM_VERSION "${CMAKE_CXX_COMPILER_VERSION}") + find_program(LLVM_SYMBOLIZER llvm-symbolizer + NAMES llvm-symbolizer-${LLVM_VERSION} llvm-symbolizer + DOC "symbolizer to use in sanitize tools") + if(NOT LLVM_SYMBOLIZER) + string(REGEX REPLACE "([0-9]+)\\.[0-9]+.*" "\\1" LLVM_VERSION "${CMAKE_CXX_COMPILER_VERSION}") + find_program(LLVM_SYMBOLIZER llvm-symbolizer + NAMES llvm-symbolizer-${LLVM_VERSION} llvm-symbolizer + DOC "symbolizer to use in sanitize tools") + if(NOT LLVM_SYMBOLIZER) + message(FATAL_ERROR "could not find symbolizer. LLVM_VERSION=${LLVM_VERSION}") + endif() + endif() + + set(ss) # string to report enabled sanitizers + + if(${_c4_uprefix}ASAN) + set(ss "asan") + set(${_c4_uprefix}ASAN_CFLAGS "-O1 -g -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "compile flags for clang address sanitizer: https://clang.llvm.org/docs/AddressSanitizer.html") + set(${_c4_uprefix}ASAN_LFLAGS "-g -fsanitize=address" CACHE STRING "linker flags for clang address sanitizer: https://clang.llvm.org/docs/AddressSanitizer.html") + set(${_c4_uprefix}ASAN_RENV "env ASAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER} ASAN_OPTIONS=symbolize=1" CACHE STRING "run environment for clang address sanitizer: https://clang.llvm.org/docs/AddressSanitizer.html") + # the flags are strings; we need to separate them into a list + # to prevent cmake from quoting them when passing to the targets + separate_arguments(${_c4_uprefix}ASAN_CFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}ASAN_CFLAGS}) + separate_arguments(${_c4_uprefix}ASAN_LFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}ASAN_LFLAGS}) + endif() + + if(${_c4_uprefix}TSAN) + set(ss "${ss} tsan") + set(${_c4_uprefix}TSAN_CFLAGS "-O1 -g -fsanitize=thread -fno-omit-frame-pointer" CACHE STRING "compile flags for clang thread sanitizer: https://clang.llvm.org/docs/ThreadSanitizer.html") + set(${_c4_uprefix}TSAN_LFLAGS "-g -fsanitize=thread" CACHE STRING "linker flags for clang thread sanitizer: https://clang.llvm.org/docs/ThreadSanitizer.html") + set(${_c4_uprefix}TSAN_RENV "env TSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER} TSAN_OPTIONS=symbolize=1" CACHE STRING "run environment for clang thread sanitizer: https://clang.llvm.org/docs/ThreadSanitizer.html") + separate_arguments(${_c4_uprefix}TSAN_CFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}TSAN_CFLAGS}) + separate_arguments(${_c4_uprefix}TSAN_LFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}TSAN_LFLAGS}) + endif() + + if(${_c4_uprefix}MSAN) + set(ss "${ss} msan") + set(${_c4_uprefix}MSAN_CFLAGS "-O1 -g -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "compile flags for clang memory sanitizer: https://clang.llvm.org/docs/MemorySanitizer.html") + set(${_c4_uprefix}MSAN_LFLAGS "-g -fsanitize=memory" CACHE STRING "linker flags for clang memory sanitizer: https://clang.llvm.org/docs/MemorySanitizer.html") + set(${_c4_uprefix}MSAN_RENV "env MSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER} MSAN_OPTIONS=symbolize=1" CACHE STRING "run environment for clang memory sanitizer: https://clang.llvm.org/docs/MemorySanitizer.html") + separate_arguments(${_c4_uprefix}MSAN_CFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}MSAN_CFLAGS}) + separate_arguments(${_c4_uprefix}MSAN_LFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}MSAN_LFLAGS}) + endif() + + if(${_c4_uprefix}UBSAN) + set(ss "${ss} ubsan") + set(${_c4_uprefix}UBSAN_CFLAGS "-g -fsanitize=undefined" CACHE STRING "compile flags for clang undefined behaviour sanitizer: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html") + set(${_c4_uprefix}UBSAN_LFLAGS "-g -fsanitize=undefined" CACHE STRING "linker flags for clang undefined behaviour sanitizer: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html") + set(${_c4_uprefix}UBSAN_RENV "env UBSAN_SYMBOLIZER_PATH=${LLVM_SYMBOLIZER} UBSAN_OPTIONS='symbolize=1 print_stacktrace=1'" CACHE STRING "run environment for clang undefined behaviour sanitizer: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html") + separate_arguments(${_c4_uprefix}UBSAN_CFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}UBSAN_CFLAGS}) + separate_arguments(${_c4_uprefix}UBSAN_LFLAGS_SEP UNIX_COMMAND ${${_c4_uprefix}UBSAN_LFLAGS}) + endif() + + c4_dbg("enabled clang sanitizers: ${ss}") + endif() # ${_c4_uprefix}SANITIZE + +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_sanitize_get_target_command name which_sanitizer_ output) + string(TOUPPER ${which_sanitizer_} which_sanitizer) + if("${which_sanitizer}" STREQUAL ASAN) + elseif("${which_sanitizer}" STREQUAL TSAN) + elseif("${which_sanitizer}" STREQUAL MSAN) + elseif("${which_sanitizer}" STREQUAL UBSAN) + else() + message(FATAL_ERROR "the sanitizer must be one of: ASAN, TSAN, MSAN, UBSAN") + endif() + separate_arguments(cmd UNIX_COMMAND "${${_c4_uprefix}${which_sanitizer}_RENV} ${name}") + set(${output} ${cmd} PARENT_SCOPE) +endfunction() + +function(_sanitize_set_target_folder tgt folder) + if(folder) + set_target_properties(${tgt} PROPERTIES FOLDER "${folder}") + endif() +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_sanitize_target name) + set(opt0arg + LIBRARY + EXECUTABLE + ) + set(opt1arg + OUTPUT_TARGET_NAMES + FOLDER + ) + set(optnarg + SOURCES + INC_DIRS # TODO public, interface, private + LIBS # TODO public, interface, private + LIB_DIRS # TODO public, interface, private + DEFS # TODO public, interface, private + CFLAGS # TODO public, interface, private + ) + cmake_parse_arguments("" "${opt0arg}" "${opt1arg}" "${optnarg}" ${ARGN}) + + if((NOT _LIBRARY) AND (NOT _EXECUTABLE)) + c4_err("either LIBRARY or EXECUTABLE must be specified") + endif() + + if(${_c4_uprefix}SANITIZE AND NOT TARGET ${_c4_lprefix}sanitize) + add_custom_target(${_c4_lprefix}sanitize) + _sanitize_set_target_folder(${_c4_lprefix}sanitize "${_FOLDER}") + endif() + if(${_c4_uprefix}ASAN AND NOT TARGET ${_c4_lprefix}asan-all) + add_custom_target(${_c4_lprefix}asan-all) + add_dependencies(${_c4_lprefix}sanitize ${_c4_lprefix}asan-all) + _sanitize_set_target_folder(${_c4_lprefix}asan-all "${_FOLDER}") + endif() + if(${_c4_uprefix}MSAN AND NOT TARGET ${_c4_lprefix}msan-all) + add_custom_target(${_c4_lprefix}msan-all) + add_dependencies(${_c4_lprefix}sanitize ${_c4_lprefix}msan-all) + _sanitize_set_target_folder(${_c4_lprefix}msan-all "${_FOLDER}") + endif() + if(${_c4_uprefix}TSAN AND NOT TARGET ${_c4_lprefix}tsan-all) + add_custom_target(${_c4_lprefix}tsan-all) + add_dependencies(${_c4_lprefix}sanitize ${_c4_lprefix}tsan-all) + _sanitize_set_target_folder(${_c4_lprefix}tsan-all "${_FOLDER}") + endif() + if(${_c4_uprefix}UBSAN AND NOT TARGET ${_c4_lprefix}ubsan-all) + add_custom_target(${_c4_lprefix}ubsan-all) + add_dependencies(${_c4_lprefix}sanitize ${_c4_lprefix}ubsan-all) + _sanitize_set_target_folder(${_c4_lprefix}ubsan-all "${_FOLDER}") + endif() + + if(${_c4_uprefix}ASAN OR ${_c4_uprefix}MSAN OR ${_c4_uprefix}TSAN OR ${_c4_uprefix}UBSAN) + add_custom_target(${name}-sanitize-all) + _sanitize_set_target_folder(${name}-sanitize-all "${_FOLDER}") + endif() + + set(targets) + + # https://clang.llvm.org/docs/AddressSanitizer.html + if(${_c4_uprefix}ASAN) + if(${_LIBRARY}) + add_library(${name}-asan EXCLUDE_FROM_ALL ${_SOURCES}) + elseif(${_EXECUTABLE}) + add_executable(${name}-asan EXCLUDE_FROM_ALL ${_SOURCES}) + endif() + _sanitize_set_target_folder(${name}-asan "${_FOLDER}") + list(APPEND targets ${name}-asan) + target_include_directories(${name}-asan PUBLIC ${_INC_DIRS}) + set(_real_libs) + foreach(_l ${_LIBS}) + if(TARGET ${_l}-asan) + list(APPEND _real_libs ${_l}-asan) + else() + list(APPEND _real_libs ${_l}) + endif() + endforeach() + target_link_libraries(${name}-asan PUBLIC ${_real_libs}) + target_compile_definitions(${name}-asan PUBLIC ${_DEFS}) + target_compile_options(${name}-asan PUBLIC ${_CFLAGS} ${${_c4_uprefix}ASAN_CFLAGS_SEP}) + # http://stackoverflow.com/questions/25043458/does-cmake-have-something-like-target-link-options + target_link_libraries(${name}-asan PUBLIC ${${_c4_uprefix}ASAN_LFLAGS_SEP}) + add_dependencies(${_c4_lprefix}asan-all ${name}-asan) + add_dependencies(${name}-sanitize-all ${name}-asan) + endif() + + # https://clang.llvm.org/docs/ThreadSanitizer.html + if(${_c4_uprefix}TSAN) + if(${_LIBRARY}) + add_library(${name}-tsan EXCLUDE_FROM_ALL ${_SOURCES}) + elseif(${_EXECUTABLE}) + add_executable(${name}-tsan EXCLUDE_FROM_ALL ${_SOURCES}) + endif() + _sanitize_set_target_folder(${name}-tsan "${_FOLDER}") + list(APPEND targets ${name}-tsan) + target_include_directories(${name}-tsan PUBLIC ${_INC_DIRS}) + set(_real_libs) + foreach(_l ${_LIBS}) + if(TARGET ${_l}-tsan) + list(APPEND _real_libs ${_l}-tsan) + else() + list(APPEND _real_libs ${_l}) + endif() + endforeach() + target_link_libraries(${name}-tsan PUBLIC ${_real_libs}) + target_compile_definitions(${name}-tsan PUBLIC ${_DEFS}) + target_compile_options(${name}-tsan PUBLIC ${_CFLAGS} ${${_c4_uprefix}TSAN_CFLAGS_SEP}) + # http://stackoverflow.com/questions/25043458/does-cmake-have-something-like-target-link-options + target_link_libraries(${name}-tsan PUBLIC ${${_c4_uprefix}TSAN_LFLAGS_SEP}) + add_dependencies(${_c4_lprefix}tsan-all ${name}-tsan) + add_dependencies(${name}-sanitize-all ${name}-tsan) + endif() + + # https://clang.llvm.org/docs/MemorySanitizer.html + if(${_c4_uprefix}MSAN) + if(${_LIBRARY}) + add_library(${name}-msan EXCLUDE_FROM_ALL ${_SOURCES}) + elseif(${_EXECUTABLE}) + add_executable(${name}-msan EXCLUDE_FROM_ALL ${_SOURCES}) + endif() + _sanitize_set_target_folder(${name}-msan "${_FOLDER}") + list(APPEND targets ${name}-msan) + target_include_directories(${name}-msan PUBLIC ${_INC_DIRS}) + set(_real_libs) + foreach(_l ${_LIBS}) + if(TARGET ${_l}-msan) + list(APPEND _real_libs ${_l}-msan) + else() + list(APPEND _real_libs ${_l}) + endif() + endforeach() + target_link_libraries(${name}-msan PUBLIC ${_real_libs}) + target_compile_definitions(${name}-msan PUBLIC ${_DEFS}) + target_compile_options(${name}-msan PUBLIC ${_CFLAGS} ${${_c4_uprefix}MSAN_CFLAGS_SEP}) + # http://stackoverflow.com/questions/25043458/does-cmake-have-something-like-target-link-options + target_link_libraries(${name}-msan PUBLIC ${${_c4_uprefix}MSAN_LFLAGS_SEP}) + add_dependencies(${_c4_lprefix}msan-all ${name}-msan) + add_dependencies(${name}-sanitize-all ${name}-msan) + endif() + + # https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html + if(${_c4_uprefix}UBSAN) + if(${_LIBRARY}) + add_library(${name}-ubsan EXCLUDE_FROM_ALL ${_SOURCES}) + elseif(${_EXECUTABLE}) + add_executable(${name}-ubsan EXCLUDE_FROM_ALL ${_SOURCES}) + endif() + _sanitize_set_target_folder(${name}-ubsan "${_FOLDER}") + list(APPEND targets ${name}-ubsan) + target_include_directories(${name}-ubsan PUBLIC ${_INC_DIRS}) + set(_real_libs) + foreach(_l ${_LIBS}) + if(TARGET ${_l}-ubsan) + list(APPEND _real_libs ${_l}-ubsan) + else() + list(APPEND _real_libs ${_l}) + endif() + endforeach() + target_link_libraries(${name}-ubsan PUBLIC ${_real_libs}) + target_compile_definitions(${name}-ubsan PUBLIC ${_DEFS}) + target_compile_options(${name}-ubsan PUBLIC ${_CFLAGS} ${${_c4_uprefix}UBSAN_CFLAGS_SEP}) + # http://stackoverflow.com/questions/25043458/does-cmake-have-something-like-target-link-options + target_link_libraries(${name}-ubsan PUBLIC ${${_c4_uprefix}UBSAN_LFLAGS_SEP}) + add_dependencies(${_c4_lprefix}ubsan-all ${name}-ubsan) + add_dependencies(${name}-sanitize-all ${name}-ubsan) + endif() + + if(_OUTPUT_TARGET_NAMES) + set(${_OUTPUT_TARGET_NAMES} ${targets} PARENT_SCOPE) + endif() +endfunction() + + +endif(NOT _c4_sanitize_target_included) diff --git a/thirdparty/ryml/ext/c4core/cmake/c4StaticAnalysis.cmake b/thirdparty/ryml/ext/c4core/cmake/c4StaticAnalysis.cmake new file mode 100644 index 000000000..06de5ca2f --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4StaticAnalysis.cmake @@ -0,0 +1,154 @@ +include(PVS-Studio) +include(GetFlags) +include(c4GetTargetPropertyRecursive) + + +function(_c4sta_default_if_not_set var dft) + if("${${var}}" STREQUAL "") + set(${var} "${dft}" PARENT_SCOPE) + endif() +endfunction() + + +function(c4_setup_static_analysis umbrella_option) + if(WIN32) + c4_dbg("no static analyzer available in WIN32") + return() + endif() + if("${CMAKE_BUILD_TYPE}" STREQUAL "Coverage") + c4_dbg("Coverage build: disabling static analyzers") + return() + endif() + _c4sta_default_if_not_set(C4_LINT ${umbrella_option}) + _c4sta_default_if_not_set(C4_LINT_TESTS ${umbrella_option}) + _c4sta_default_if_not_set(C4_LINT_CLANG_TIDY ${umbrella_option}) + _c4sta_default_if_not_set(C4_LINT_PVS_STUDIO OFF) + # option to turn lints on/off + cmake_dependent_option(${_c4_uprefix}LINT "add static analyzer targets" ${C4_LINT} ${umbrella_option} OFF) + cmake_dependent_option(${_c4_uprefix}LINT_TESTS "add tests to run static analyzer targets" ${C4_LINT_TESTS} ${umbrella_option} OFF) + # options for individual lints - contingent on linting on/off + cmake_dependent_option(${_c4_uprefix}LINT_CLANG_TIDY "use the clang-tidy static analyzer" ${C4_LINT_CLANG_TIDY} "${_c4_uprefix}LINT" ON) + cmake_dependent_option(${_c4_uprefix}LINT_PVS_STUDIO "use the PVS-Studio static analyzer https://www.viva64.com/en/b/0457/" ${C4_LINT_PVS_STUDIO} "${_c4_uprefix}LINT" OFF) + if(${_c4_uprefix}LINT_CLANG_TIDY) + find_program(CLANG_TIDY clang-tidy) + endif() + if(${_c4_uprefix}LINT_PVS_STUDIO) + set(${_c4_uprefix}LINT_PVS_STUDIO_FORMAT "errorfile" CACHE STRING "PVS-Studio output format. Choices: xml,csv,errorfile(like gcc/clang),tasklist(qtcreator)") + endif() + # + set(sa) + if(${_c4_uprefix}LINT_CLANG_TIDY) + set(sa "clang_tidy") + endif() + if(${_c4_uprefix}LINT_PVS_STUDIO) + set(sa "${sa} PVS-Studio") + endif() + if(sa) + c4_dbg("enabled static analyzers: ${sa}") + endif() +endfunction() + + +function(c4_static_analysis_target target_name folder generated_targets) + set(any_linter OFF) + if(${_c4_uprefix}LINT_CLANG_TIDY OR ${_c4_uprefix}LINT_PVS_STUDIO) + set(any_linter ON) + endif() + if(${_c4_uprefix}LINT AND any_linter) + # umbrella target for running all linters for this particular target + if(any_linter AND NOT TARGET ${_c4_lprefix}lint-all) + add_custom_target(${_c4_lprefix}lint-all) + if(folder) + #message(STATUS "${target_name}: folder=${folder}") + set_target_properties(${_c4_lprefix}lint-all PROPERTIES FOLDER "${folder}") + endif() + endif() + if(${_c4_uprefix}LINT_CLANG_TIDY) + c4_static_analysis_clang_tidy(${target_name} + ${target_name}-lint-clang_tidy + ${_c4_lprefix}lint-all-clang_tidy + "${folder}") + list(APPEND ${generated_targets} ${_c4_lprefix}lint-clang_tidy) + add_dependencies(${_c4_lprefix}lint-all ${_c4_lprefix}lint-all-clang_tidy) + endif() + if(${_c4_uprefix}LINT_PVS_STUDIO) + c4_static_analysis_pvs_studio(${target_name} + ${target_name}-lint-pvs_studio + ${_c4_lprefix}lint-all-pvs_studio + "${folder}") + list(APPEND ${generated_targets} ${_c4_lprefix}lint-pvs_studio) + add_dependencies(${_c4_lprefix}lint-all ${_c4_lprefix}lint-all-pvs_studio) + endif() + endif() +endfunction() + + +function(c4_static_analysis_add_tests target_name) + if(${_c4_uprefix}LINT_CLANG_TIDY AND ${_c4_uprefix}LINT_TESTS) + add_test(NAME ${target_name}-lint-clang_tidy-run + COMMAND + ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR} --target ${target_name}-lint-clang_tidy) + endif() + if(${_c4_uprefix}LINT_PVS_STUDIO AND ${_c4_uprefix}LINT_TESTS) + add_test(NAME ${target_name}-lint-pvs_studio-run + COMMAND + ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR} --target ${target_name}-lint-pvs_studio) + endif() +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_static_analysis_clang_tidy subj_target lint_target umbrella_target folder) + c4_static_analysis_clang_tidy_get_cmd(${subj_target} ${lint_target} cmd) + string(REPLACE ";" " " cmd_str "${cmd}") + add_custom_target(${lint_target} + COMMAND ${CMAKE_COMMAND} -E echo "cd ${CMAKE_CURRENT_SOURCE_DIR} ; ${cmd_str}" + COMMAND ${cmd} + VERBATIM + COMMENT "clang-tidy: analyzing sources of ${subj_target}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + if(folder) + set_target_properties(${lint_target} PROPERTIES FOLDER "${folder}") + endif() + if(NOT TARGET ${umbrella_target}) + add_custom_target(${umbrella_target}) + endif() + add_dependencies(${umbrella_target} ${lint_target}) +endfunction() + +function(c4_static_analysis_clang_tidy_get_cmd subj_target lint_target cmd) + get_target_property(_clt_all_srcs ${subj_target} SOURCES) + _c4cat_filter_srcs_hdrs("${_clt_all_srcs}" _clt_srcs) + set(result "${CLANG_TIDY}" -p ${CMAKE_BINARY_DIR} --header-filter=.* ${_clt_srcs}) + set(${cmd} ${result} PARENT_SCOPE) +endfunction() + + +#------------------------------------------------------------------------------ +function(c4_static_analysis_pvs_studio subj_target lint_target umbrella_target folder) + c4_get_target_property_recursive(_c4al_pvs_incs ${subj_target} INCLUDE_DIRECTORIES) + c4_get_include_flags(_c4al_pvs_incs ${_c4al_pvs_incs}) + separate_arguments(_c4al_cxx_flags_sep UNIX_COMMAND "${CMAKE_CXX_FLAGS} ${_c4al_pvs_incs}") + separate_arguments(_c4al_c_flags_sep UNIX_COMMAND "${CMAKE_C_FLAGS} ${_c4al_pvs_incs}") + pvs_studio_add_target(TARGET ${lint_target} + ALL # indicates that the analysis starts when you build the project + #PREPROCESSOR ${_c4al_preproc} + FORMAT tasklist + LOG "${CMAKE_CURRENT_BINARY_DIR}/${subj_target}.pvs-analysis.tasks" + ANALYZE ${name} #main_target subtarget:path/to/subtarget + CXX_FLAGS ${_c4al_cxx_flags_sep} + C_FLAGS ${_c4al_c_flags_sep} + #CONFIG "/path/to/PVS-Studio.cfg" + ) + if(folder) + set_target_properties(${lint_target} PROPERTIES FOLDER "${folder}") + endif() + if(NOT TARGET ${umbrella_target}) + add_custom_target(${umbrella_target}) + endif() + add_dependencies(${umbrella_target} ${lint_target}) +endfunction() + +function(c4_static_analysis_pvs_studio_get_cmd subj_target lint_target cmd) + set(${cmd} $<RULE_LAUNCH_CUSTOM:${subj_target}> PARENT_SCOPE) +endfunction() diff --git a/thirdparty/ryml/ext/c4core/cmake/c4stlAddTarget.cmake b/thirdparty/ryml/ext/c4core/cmake/c4stlAddTarget.cmake new file mode 100644 index 000000000..07835977b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/c4stlAddTarget.cmake @@ -0,0 +1,5 @@ +include(c4project) + +function(c4stl_add_target name) + c4_add_target(c4stl ${name} ${ARGN}) +endfunction() # c4stl_add_target diff --git a/thirdparty/ryml/ext/c4core/cmake/compat/c4/gcc-4.8.hpp b/thirdparty/ryml/ext/c4core/cmake/compat/c4/gcc-4.8.hpp new file mode 100644 index 000000000..83cad6256 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/compat/c4/gcc-4.8.hpp @@ -0,0 +1,69 @@ +#ifndef _C4_COMPAT_GCC_4_8_HPP_ +#define _C4_COMPAT_GCC_4_8_HPP_ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 8 +/* STL polyfills for old GNU compilers */ + +_Pragma("GCC diagnostic ignored \"-Wshadow\"") +_Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") + +#if __cplusplus +#include <cstdint> +#include <type_traits> + +namespace std { + +template<typename _Tp> +struct is_trivially_copyable : public integral_constant<bool, + is_destructible<_Tp>::value && __has_trivial_destructor(_Tp) && + (__has_trivial_constructor(_Tp) || __has_trivial_copy(_Tp) || __has_trivial_assign(_Tp))> +{ }; + +template<typename _Tp> +using is_trivially_copy_constructible = has_trivial_copy_constructor<_Tp>; + +template<typename _Tp> +using is_trivially_default_constructible = has_trivial_default_constructor<_Tp>; + +template<typename _Tp> +using is_trivially_copy_assignable = has_trivial_copy_assign<_Tp>; + +/* not supported */ +template<typename _Tp> +struct is_trivially_move_constructible : false_type +{ }; + +/* not supported */ +template<typename _Tp> +struct is_trivially_move_assignable : false_type +{ }; + +inline void *align(size_t __align, size_t __size, void*& __ptr, size_t& __space) noexcept +{ + if (__space < __size) + return nullptr; + const auto __intptr = reinterpret_cast<uintptr_t>(__ptr); + const auto __aligned = (__intptr - 1u + __align) & -__align; + const auto __diff = __aligned - __intptr; + if (__diff > (__space - __size)) + return nullptr; + else + { + __space -= __diff; + return __ptr = reinterpret_cast<void*>(__aligned); + } +} +typedef long double max_align_t ; + +} +#else // __cplusplus + +#include <string.h> +// see https://sourceware.org/bugzilla/show_bug.cgi?id=25399 (ubuntu gcc-4.8) +#define memset(s, c, count) __builtin_memset(s, c, count) + +#endif // __cplusplus + +#endif // __GNUC__ == 4 && __GNUC_MINOR__ >= 8 + +#endif // _C4_COMPAT_GCC_4_8_HPP_ diff --git a/thirdparty/ryml/ext/c4core/cmake/compat/gtest_gcc-4.8.patch b/thirdparty/ryml/ext/c4core/cmake/compat/gtest_gcc-4.8.patch new file mode 100644 index 000000000..07f0ca577 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/compat/gtest_gcc-4.8.patch @@ -0,0 +1,97 @@ +Multi-line macros support is not guaranteed with gcc-4.8. + +This uses temporary objects to work-arround this limitation, main drawback is +that compared code is not displayed in message anymore (only "val" placeholders). + +--- googletest/include/gtest/gtest.h ++++ googletest/include/gtest/gtest.h +@@ -2040,6 +2040,80 @@ class TestWithParam : public Test, publi + // ASSERT_LT(i, array_size); + // ASSERT_GT(records.size(), 0) << "There is no record left."; + ++#if __GNUC__ == 4 && __GNUC_MINOR__ >= 8 ++/* ++ * multi-line macros support is not guaranteed with gcc-4.8. ++ * This uses temporary objects to work-arround this limitation, main drawback is ++ * that compared code is not displayed in message anymore (only "val" placeholders) ++ */ ++ ++enum class CompatExpectSelector { EQ, NE, LE, LT, GE, GT }; ++ ++template <CompatExpectSelector T> ++struct CompatExpect ++{ ++ const char *file; ++ int line; ++ ::testing::AssertionResult gtest_ar = AssertionSuccess(); ++ ::testing::Message msg; ++ ++ CompatExpect(const char *file, int line) : file(file), line(line) {} ++ ~CompatExpect() { ++ if (!gtest_ar) ++ GTEST_MESSAGE_AT_(file, line, gtest_ar.failure_message(), ::testing::TestPartResult::kNonFatalFailure) << msg; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::EQ, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::EqHelper::Compare("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::NE, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperNE("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::LE, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperLE("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::LT, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperLT("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::GE, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperGE("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1, typename T2, CompatExpectSelector SEL = T, typename std::enable_if<SEL == CompatExpectSelector::GT, int>::type = 0> ++ CompatExpect<T> &operator ()(const T1 &val1, const T2 &val2) { ++ gtest_ar = ::testing::internal::CmpHelperGT("val1", "val2", val1, val2); ++ return *this; ++ } ++ ++ template <typename T1> ++ CompatExpect &operator << (const T1 &t) { ++ msg << t; ++ return *this; ++ } ++}; ++#define EXPECT_EQ ::testing::CompatExpect<::testing::CompatExpectSelector::EQ>{__FILE__,__LINE__} ++#define EXPECT_NE ::testing::CompatExpect<::testing::CompatExpectSelector::NE>{__FILE__,__LINE__} ++#define EXPECT_LE ::testing::CompatExpect<::testing::CompatExpectSelector::LE>{__FILE__,__LINE__} ++#define EXPECT_LT ::testing::CompatExpect<::testing::CompatExpectSelector::LT>{__FILE__,__LINE__} ++#define EXPECT_GE ::testing::CompatExpect<::testing::CompatExpectSelector::GE>{__FILE__,__LINE__} ++#define EXPECT_GT ::testing::CompatExpect<::testing::CompatExpectSelector::GT>{__FILE__,__LINE__} ++ ++#else ++ + #define EXPECT_EQ(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) + #define EXPECT_NE(val1, val2) \ +@@ -2053,6 +2127,8 @@ class TestWithParam : public Test, publi + #define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + ++#endif ++ + #define GTEST_ASSERT_EQ(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2) + #define GTEST_ASSERT_NE(val1, val2) \ diff --git a/thirdparty/ryml/ext/c4core/cmake/requirements_doc.txt b/thirdparty/ryml/ext/c4core/cmake/requirements_doc.txt new file mode 100644 index 000000000..baeb2ce4f --- /dev/null +++ b/thirdparty/ryml/ext/c4core/cmake/requirements_doc.txt @@ -0,0 +1,3 @@ +sphinx +sphinx_rtd_theme +breathe diff --git a/thirdparty/ryml/ext/c4core/compat.cmake b/thirdparty/ryml/ext/c4core/compat.cmake new file mode 100644 index 000000000..186b84e0b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/compat.cmake @@ -0,0 +1,16 @@ + +# old gcc-4.8 support +if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND + (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8) AND + (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)) + + c4_install_files( + "${CMAKE_CURRENT_LIST_DIR}/cmake/compat/c4/gcc-4.8.hpp" + "include" + "${CMAKE_CURRENT_LIST_DIR}/cmake/compat") + + # c++17 compiler required + set(C4CORE_BUILD_BENCHMARKS OFF CACHE BOOL "" FORCE) + # LLVM required + set(C4CORE_SANITIZE OFF CACHE BOOL "" FORCE) +endif() diff --git a/thirdparty/ryml/ext/c4core/src/c4/allocator.hpp b/thirdparty/ryml/ext/c4core/src/c4/allocator.hpp new file mode 100644 index 000000000..d221341c5 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/allocator.hpp @@ -0,0 +1,405 @@ +#ifndef _C4_ALLOCATOR_HPP_ +#define _C4_ALLOCATOR_HPP_ + +#include "c4/memory_resource.hpp" +#include "c4/ctor_dtor.hpp" + +#include <memory> // std::allocator_traits +#include <type_traits> + +/** @file allocator.hpp Contains classes to make typeful allocations (note + * that memory resources are typeless) */ + +/** @defgroup mem_res_providers Memory resource providers + * @brief Policy classes which provide a memory resource for + * use in an allocator. + * @ingroup memory + */ + +/** @defgroup allocators Allocators + * @brief Lightweight classes that act as handles to specific memory + * resources and provide typeful memory. + * @ingroup memory + */ + +namespace c4 { + +namespace detail { +template<class T> inline size_t size_for (size_t num_objs) noexcept { return num_objs * sizeof(T); } +template< > inline size_t size_for<void>(size_t num_objs) noexcept { return num_objs; } +} // namespace detail + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** provides a per-allocator memory resource + * @ingroup mem_res_providers */ +class MemRes +{ +public: + + MemRes() : m_resource(get_memory_resource()) {} + MemRes(MemoryResource* r) noexcept : m_resource(r ? r : get_memory_resource()) {} + + inline MemoryResource* resource() const { return m_resource; } + +private: + + MemoryResource* m_resource; + +}; + + +/** the allocators using this will default to the global memory resource + * @ingroup mem_res_providers */ +class MemResGlobal +{ +public: + + MemResGlobal() {} + MemResGlobal(MemoryResource* r) noexcept { C4_UNUSED(r); C4_ASSERT(r == get_memory_resource()); } + + inline MemoryResource* resource() const { return get_memory_resource(); } +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace detail { +template<class MemRes> +struct _AllocatorUtil; + +template<class T, class ...Args> +struct has_no_alloc + : public std::integral_constant<bool, + !(std::uses_allocator<T, MemoryResource*>::value) + && std::is_constructible<T, Args...>::value> {}; + +// std::uses_allocator_v<U, MemoryResource> && std::is_constructible<U, std::allocator_arg_t, MemoryResource*, Args...> +// ie can construct(std::allocator_arg_t, MemoryResource*, Args...) +template<class T, class ...Args> +struct has_alloc_arg + : public std::integral_constant<bool, + std::uses_allocator<T, MemoryResource*>::value + && std::is_constructible<T, std::allocator_arg_t, MemoryResource*, Args...>::value> {}; +// std::uses_allocator<U> && std::is_constructible<U, Args..., MemoryResource*> +// ie, can construct(Args..., MemoryResource*) +template<class T, class ...Args> +struct has_alloc + : public std::integral_constant<bool, + std::uses_allocator<T, MemoryResource*>::value + && std::is_constructible<T, Args..., MemoryResource*>::value> {}; + +} // namespace detail + + +template<class MemRes> +struct detail::_AllocatorUtil : public MemRes +{ + using MemRes::MemRes; + + /** for construct: + * @see http://en.cppreference.com/w/cpp/experimental/polymorphic_allocator/construct */ + + // 1. types with no allocators + template <class U, class... Args> + C4_ALWAYS_INLINE typename std::enable_if<detail::has_no_alloc<U, Args...>::value, void>::type + construct(U *ptr, Args &&...args) + { + c4::construct(ptr, std::forward<Args>(args)...); + } + template<class U, class I, class... Args> + C4_ALWAYS_INLINE typename std::enable_if<detail::has_no_alloc<U, Args...>::value, void>::type + construct_n(U* ptr, I n, Args&&... args) + { + c4::construct_n(ptr, n, std::forward<Args>(args)...); + } + + // 2. types using allocators (ie, containers) + + // 2.1. can construct(std::allocator_arg_t, MemoryResource*, Args...) + template<class U, class... Args> + C4_ALWAYS_INLINE typename std::enable_if<detail::has_alloc_arg<U, Args...>::value, void>::type + construct(U* ptr, Args&&... args) + { + c4::construct(ptr, std::allocator_arg, this->resource(), std::forward<Args>(args)...); + } + template<class U, class I, class... Args> + C4_ALWAYS_INLINE typename std::enable_if<detail::has_alloc_arg<U, Args...>::value, void>::type + construct_n(U* ptr, I n, Args&&... args) + { + c4::construct_n(ptr, n, std::allocator_arg, this->resource(), std::forward<Args>(args)...); + } + + // 2.2. can construct(Args..., MemoryResource*) + template<class U, class... Args> + C4_ALWAYS_INLINE typename std::enable_if<detail::has_alloc<U, Args...>::value, void>::type + construct(U* ptr, Args&&... args) + { + c4::construct(ptr, std::forward<Args>(args)..., this->resource()); + } + template<class U, class I, class... Args> + C4_ALWAYS_INLINE typename std::enable_if<detail::has_alloc<U, Args...>::value, void>::type + construct_n(U* ptr, I n, Args&&... args) + { + c4::construct_n(ptr, n, std::forward<Args>(args)..., this->resource()); + } + + template<class U> + static C4_ALWAYS_INLINE void destroy(U* ptr) + { + c4::destroy(ptr); + } + template<class U, class I> + static C4_ALWAYS_INLINE void destroy_n(U* ptr, I n) + { + c4::destroy_n(ptr, n); + } +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** An allocator is simply a proxy to a memory resource. + * @param T + * @param MemResProvider + * @ingroup allocators */ +template<class T, class MemResProvider=MemResGlobal> +class Allocator : public detail::_AllocatorUtil<MemResProvider> +{ +public: + + using impl_type = detail::_AllocatorUtil<MemResProvider>; + + using value_type = T; + using pointer = T*; + using const_pointer = T const*; + using reference = T&; + using const_reference = T const&; + using size_type = size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assigment = std::true_type; + +public: + + template<class U, class MRProv> + bool operator== (Allocator<U, MRProv> const& that) const + { + return this->resource() == that.resource(); + } + template<class U, class MRProv> + bool operator!= (Allocator<U, MRProv> const& that) const + { + return this->resource() != that.resource(); + } + +public: + + template<class U, class MRProv> friend class Allocator; + template<class U> + struct rebind + { + using other = Allocator<U, MemResProvider>; + }; + template<class U> + typename rebind<U>::other rebound() + { + return typename rebind<U>::other(*this); + } + +public: + + using impl_type::impl_type; + Allocator() : impl_type() {} // VS demands this + + template<class U> Allocator(Allocator<U, MemResProvider> const& that) : impl_type(that.resource()) {} + + Allocator(Allocator const&) = default; + Allocator(Allocator &&) = default; + + Allocator& operator= (Allocator const&) = default; // WTF? why? @see http://en.cppreference.com/w/cpp/memory/polymorphic_allocator + Allocator& operator= (Allocator &&) = default; + + /** returns a default-constructed polymorphic allocator object + * @see http://en.cppreference.com/w/cpp/memory/polymorphic_allocator/select_on_container_copy_construction */ + Allocator select_on_container_copy_construct() const { return Allocator(*this); } + + T* allocate(size_t num_objs, size_t alignment=alignof(T)) + { + C4_ASSERT(this->resource() != nullptr); + C4_ASSERT(alignment >= alignof(T)); + void* vmem = this->resource()->allocate(detail::size_for<T>(num_objs), alignment); + T* mem = static_cast<T*>(vmem); + return mem; + } + + void deallocate(T * ptr, size_t num_objs, size_t alignment=alignof(T)) + { + C4_ASSERT(this->resource() != nullptr); + C4_ASSERT(alignment>= alignof(T)); + this->resource()->deallocate(ptr, detail::size_for<T>(num_objs), alignment); + } + + T* reallocate(T* ptr, size_t oldnum, size_t newnum, size_t alignment=alignof(T)) + { + C4_ASSERT(this->resource() != nullptr); + C4_ASSERT(alignment >= alignof(T)); + void* vmem = this->resource()->reallocate(ptr, detail::size_for<T>(oldnum), detail::size_for<T>(newnum), alignment); + T* mem = static_cast<T*>(vmem); + return mem; + } + +}; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** @ingroup allocators */ +template<class T, size_t N=16, size_t Alignment=alignof(T), class MemResProvider=MemResGlobal> +class SmallAllocator : public detail::_AllocatorUtil<MemResProvider> +{ + static_assert(Alignment >= alignof(T), "invalid alignment"); + + using impl_type = detail::_AllocatorUtil<MemResProvider>; + + alignas(Alignment) char m_arr[N * sizeof(T)]; + size_t m_num{0}; + +public: + + using value_type = T; + using pointer = T*; + using const_pointer = T const*; + using reference = T&; + using const_reference = T const&; + using size_type = size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assigment = std::true_type; + + template<class U> + bool operator== (SmallAllocator<U,N,Alignment,MemResProvider> const&) const + { + return false; + } + template<class U> + bool operator!= (SmallAllocator<U,N,Alignment,MemResProvider> const&) const + { + return true; + } + +public: + + template<class U, size_t, size_t, class> friend class SmallAllocator; + template<class U> + struct rebind + { + using other = SmallAllocator<U, N, alignof(U), MemResProvider>; + }; + template<class U> + typename rebind<U>::other rebound() + { + return typename rebind<U>::other(*this); + } + +public: + + using impl_type::impl_type; + SmallAllocator() : impl_type() {} // VS demands this + + template<class U, size_t N2, size_t A2, class MP2> + SmallAllocator(SmallAllocator<U,N2,A2,MP2> const& that) : impl_type(that.resource()) + { + C4_ASSERT(that.m_num == 0); + } + + SmallAllocator(SmallAllocator const&) = default; + SmallAllocator(SmallAllocator &&) = default; + + SmallAllocator& operator= (SmallAllocator const&) = default; // WTF? why? @see http://en.cppreference.com/w/cpp/memory/polymorphic_allocator + SmallAllocator& operator= (SmallAllocator &&) = default; + + /** returns a default-constructed polymorphic allocator object + * @see http://en.cppreference.com/w/cpp/memory/polymorphic_allocator/select_on_container_copy_construction */ + SmallAllocator select_on_container_copy_construct() const { return SmallAllocator(*this); } + + T* allocate(size_t num_objs, size_t alignment=Alignment) + { + C4_ASSERT(this->resource() != nullptr); + C4_ASSERT(alignment >= alignof(T)); + void *vmem; + if(m_num + num_objs <= N) + { + vmem = (m_arr + m_num * sizeof(T)); + } + else + { + vmem = this->resource()->allocate(num_objs * sizeof(T), alignment); + } + m_num += num_objs; + T *mem = static_cast<T*>(vmem); + return mem; + } + + void deallocate(T * ptr, size_t num_objs, size_t alignment=Alignment) + { + C4_ASSERT(m_num >= num_objs); + m_num -= num_objs; + if((char*)ptr >= m_arr && (char*)ptr < m_arr + (N * sizeof(T))) + { + return; + } + C4_ASSERT(this->resource() != nullptr); + C4_ASSERT(alignment >= alignof(T)); + this->resource()->deallocate(ptr, num_objs * sizeof(T), alignment); + } + + T* reallocate(T * ptr, size_t oldnum, size_t newnum, size_t alignment=Alignment) + { + C4_ASSERT(this->resource() != nullptr); + C4_ASSERT(alignment >= alignof(T)); + if(oldnum <= N && newnum <= N) + { + return m_arr; + } + else if(oldnum <= N && newnum > N) + { + return allocate(newnum, alignment); + } + else if(oldnum > N && newnum <= N) + { + deallocate(ptr, oldnum, alignment); + return m_arr; + } + void* vmem = this->resource()->reallocate(ptr, oldnum * sizeof(T), newnum * sizeof(T), alignment); + T* mem = static_cast<T*>(vmem); + return mem; + } + +}; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** An allocator making use of the global memory resource. + * @ingroup allocators */ +template<class T> using allocator = Allocator<T, MemResGlobal>; +/** An allocator with a per-instance memory resource + * @ingroup allocators */ +template<class T> using allocator_mr = Allocator<T, MemRes>; + +/** @ingroup allocators */ +template<class T, size_t N=16, size_t Alignment=alignof(T)> using small_allocator = SmallAllocator<T, N, Alignment, MemResGlobal>; +/** @ingroup allocators */ +template<class T, size_t N=16, size_t Alignment=alignof(T)> using small_allocator_mr = SmallAllocator<T, N, Alignment, MemRes>; + +} // namespace c4 + +#endif /* _C4_ALLOCATOR_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/base64.cpp b/thirdparty/ryml/ext/c4core/src/c4/base64.cpp new file mode 100644 index 000000000..bc75d3e0e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/base64.cpp @@ -0,0 +1,220 @@ +#include "c4/base64.hpp" + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wchar-subscripts" // array subscript is of type 'char' +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wchar-subscripts" +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +namespace c4 { + +namespace detail { + +constexpr static const char base64_sextet_to_char_[64] = { + /* 0/ 65*/ 'A', /* 1/ 66*/ 'B', /* 2/ 67*/ 'C', /* 3/ 68*/ 'D', + /* 4/ 69*/ 'E', /* 5/ 70*/ 'F', /* 6/ 71*/ 'G', /* 7/ 72*/ 'H', + /* 8/ 73*/ 'I', /* 9/ 74*/ 'J', /*10/ 75*/ 'K', /*11/ 74*/ 'L', + /*12/ 77*/ 'M', /*13/ 78*/ 'N', /*14/ 79*/ 'O', /*15/ 78*/ 'P', + /*16/ 81*/ 'Q', /*17/ 82*/ 'R', /*18/ 83*/ 'S', /*19/ 82*/ 'T', + /*20/ 85*/ 'U', /*21/ 86*/ 'V', /*22/ 87*/ 'W', /*23/ 88*/ 'X', + /*24/ 89*/ 'Y', /*25/ 90*/ 'Z', /*26/ 97*/ 'a', /*27/ 98*/ 'b', + /*28/ 99*/ 'c', /*29/100*/ 'd', /*30/101*/ 'e', /*31/102*/ 'f', + /*32/103*/ 'g', /*33/104*/ 'h', /*34/105*/ 'i', /*35/106*/ 'j', + /*36/107*/ 'k', /*37/108*/ 'l', /*38/109*/ 'm', /*39/110*/ 'n', + /*40/111*/ 'o', /*41/112*/ 'p', /*42/113*/ 'q', /*43/114*/ 'r', + /*44/115*/ 's', /*45/116*/ 't', /*46/117*/ 'u', /*47/118*/ 'v', + /*48/119*/ 'w', /*49/120*/ 'x', /*50/121*/ 'y', /*51/122*/ 'z', + /*52/ 48*/ '0', /*53/ 49*/ '1', /*54/ 50*/ '2', /*55/ 51*/ '3', + /*56/ 52*/ '4', /*57/ 53*/ '5', /*58/ 54*/ '6', /*59/ 55*/ '7', + /*60/ 56*/ '8', /*61/ 57*/ '9', /*62/ 43*/ '+', /*63/ 47*/ '/', +}; + +// https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html +constexpr static const char base64_char_to_sextet_[128] = { + #define __ char(-1) // undefined below + /* 0 NUL*/ __, /* 1 SOH*/ __, /* 2 STX*/ __, /* 3 ETX*/ __, + /* 4 EOT*/ __, /* 5 ENQ*/ __, /* 6 ACK*/ __, /* 7 BEL*/ __, + /* 8 BS */ __, /* 9 TAB*/ __, /* 10 LF */ __, /* 11 VT */ __, + /* 12 FF */ __, /* 13 CR */ __, /* 14 SO */ __, /* 15 SI */ __, + /* 16 DLE*/ __, /* 17 DC1*/ __, /* 18 DC2*/ __, /* 19 DC3*/ __, + /* 20 DC4*/ __, /* 21 NAK*/ __, /* 22 SYN*/ __, /* 23 ETB*/ __, + /* 24 CAN*/ __, /* 25 EM */ __, /* 26 SUB*/ __, /* 27 ESC*/ __, + /* 28 FS */ __, /* 29 GS */ __, /* 30 RS */ __, /* 31 US */ __, + /* 32 SPC*/ __, /* 33 ! */ __, /* 34 " */ __, /* 35 # */ __, + /* 36 $ */ __, /* 37 % */ __, /* 38 & */ __, /* 39 ' */ __, + /* 40 ( */ __, /* 41 ) */ __, /* 42 * */ __, /* 43 + */ 62, + /* 44 , */ __, /* 45 - */ __, /* 46 . */ __, /* 47 / */ 63, + /* 48 0 */ 52, /* 49 1 */ 53, /* 50 2 */ 54, /* 51 3 */ 55, + /* 52 4 */ 56, /* 53 5 */ 57, /* 54 6 */ 58, /* 55 7 */ 59, + /* 56 8 */ 60, /* 57 9 */ 61, /* 58 : */ __, /* 59 ; */ __, + /* 60 < */ __, /* 61 = */ __, /* 62 > */ __, /* 63 ? */ __, + /* 64 @ */ __, /* 65 A */ 0, /* 66 B */ 1, /* 67 C */ 2, + /* 68 D */ 3, /* 69 E */ 4, /* 70 F */ 5, /* 71 G */ 6, + /* 72 H */ 7, /* 73 I */ 8, /* 74 J */ 9, /* 75 K */ 10, + /* 76 L */ 11, /* 77 M */ 12, /* 78 N */ 13, /* 79 O */ 14, + /* 80 P */ 15, /* 81 Q */ 16, /* 82 R */ 17, /* 83 S */ 18, + /* 84 T */ 19, /* 85 U */ 20, /* 86 V */ 21, /* 87 W */ 22, + /* 88 X */ 23, /* 89 Y */ 24, /* 90 Z */ 25, /* 91 [ */ __, + /* 92 \ */ __, /* 93 ] */ __, /* 94 ^ */ __, /* 95 _ */ __, + /* 96 ` */ __, /* 97 a */ 26, /* 98 b */ 27, /* 99 c */ 28, + /*100 d */ 29, /*101 e */ 30, /*102 f */ 31, /*103 g */ 32, + /*104 h */ 33, /*105 i */ 34, /*106 j */ 35, /*107 k */ 36, + /*108 l */ 37, /*109 m */ 38, /*110 n */ 39, /*111 o */ 40, + /*112 p */ 41, /*113 q */ 42, /*114 r */ 43, /*115 s */ 44, + /*116 t */ 45, /*117 u */ 46, /*118 v */ 47, /*119 w */ 48, + /*120 x */ 49, /*121 y */ 50, /*122 z */ 51, /*123 { */ __, + /*124 | */ __, /*125 } */ __, /*126 ~ */ __, /*127 DEL*/ __, + #undef __ +}; + +#ifndef NDEBUG +void base64_test_tables() +{ + for(size_t i = 0; i < C4_COUNTOF(detail::base64_sextet_to_char_); ++i) + { + char s2c = base64_sextet_to_char_[i]; + char c2s = base64_char_to_sextet_[(int)s2c]; + C4_CHECK((size_t)c2s == i); + } + for(size_t i = 0; i < C4_COUNTOF(detail::base64_char_to_sextet_); ++i) + { + char c2s = base64_char_to_sextet_[i]; + if(c2s == char(-1)) + continue; + char s2c = base64_sextet_to_char_[(int)c2s]; + C4_CHECK((size_t)s2c == i); + } +} +#endif +} // namespace detail + + +bool base64_valid(csubstr encoded) +{ + if(encoded.len % 4) return false; + for(const char c : encoded) + { + if(c < 0/* || c >= 128*/) + return false; + if(c == '=') + continue; + if(detail::base64_char_to_sextet_[c] == char(-1)) + return false; + } + return true; +} + + +size_t base64_encode(substr buf, cblob data) +{ + #define c4append_(c) { if(pos < buf.len) { buf.str[pos] = (c); } ++pos; } + #define c4append_idx_(char_idx) \ + {\ + C4_XASSERT((char_idx) < sizeof(detail::base64_sextet_to_char_));\ + c4append_(detail::base64_sextet_to_char_[(char_idx)]);\ + } + + size_t rem, pos = 0; + constexpr const uint32_t sextet_mask = uint32_t(1 << 6) - 1; + const unsigned char *C4_RESTRICT d = (unsigned char *) data.buf; // cast to unsigned to avoid wrapping high-bits + for(rem = data.len; rem >= 3; rem -= 3, d += 3) + { + const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8) | (uint32_t(d[2]))); + c4append_idx_((val >> 18) & sextet_mask); + c4append_idx_((val >> 12) & sextet_mask); + c4append_idx_((val >> 6) & sextet_mask); + c4append_idx_((val ) & sextet_mask); + } + C4_ASSERT(rem < 3); + if(rem == 2) + { + const uint32_t val = ((uint32_t(d[0]) << 16) | (uint32_t(d[1]) << 8)); + c4append_idx_((val >> 18) & sextet_mask); + c4append_idx_((val >> 12) & sextet_mask); + c4append_idx_((val >> 6) & sextet_mask); + c4append_('='); + } + else if(rem == 1) + { + const uint32_t val = ((uint32_t(d[0]) << 16)); + c4append_idx_((val >> 18) & sextet_mask); + c4append_idx_((val >> 12) & sextet_mask); + c4append_('='); + c4append_('='); + } + return pos; + + #undef c4append_ + #undef c4append_idx_ +} + + +size_t base64_decode(csubstr encoded, blob data) +{ + #define c4append_(c) { if(wpos < data.len) { data.buf[wpos] = static_cast<c4::byte>(c); } ++wpos; } + #define c4appendval_(c, shift)\ + {\ + C4_XASSERT(c >= 0);\ + C4_XASSERT(size_t(c) < sizeof(detail::base64_char_to_sextet_));\ + val |= static_cast<uint32_t>(detail::base64_char_to_sextet_[(c)]) << ((shift) * 6);\ + } + + C4_ASSERT(base64_valid(encoded)); + C4_CHECK(encoded.len % 4 == 0); + size_t wpos = 0; // the write position + const char *C4_RESTRICT d = encoded.str; + constexpr const uint32_t full_byte = 0xff; + // process every quartet of input 6 bits --> triplet of output bytes + for(size_t rpos = 0; rpos < encoded.len; rpos += 4, d += 4) + { + if(d[2] == '=' || d[3] == '=') // skip the last quartet if it is padded + { + C4_ASSERT(d + 4 == encoded.str + encoded.len); + break; + } + uint32_t val = 0; + c4appendval_(d[3], 0); + c4appendval_(d[2], 1); + c4appendval_(d[1], 2); + c4appendval_(d[0], 3); + c4append_((val >> (2 * 8)) & full_byte); + c4append_((val >> (1 * 8)) & full_byte); + c4append_((val ) & full_byte); + } + // deal with the last quartet when it is padded + if(d == encoded.str + encoded.len) + return wpos; + if(d[2] == '=') // 2 padding chars + { + C4_ASSERT(d + 4 == encoded.str + encoded.len); + C4_ASSERT(d[3] == '='); + uint32_t val = 0; + c4appendval_(d[1], 2); + c4appendval_(d[0], 3); + c4append_((val >> (2 * 8)) & full_byte); + } + else if(d[3] == '=') // 1 padding char + { + C4_ASSERT(d + 4 == encoded.str + encoded.len); + uint32_t val = 0; + c4appendval_(d[2], 1); + c4appendval_(d[1], 2); + c4appendval_(d[0], 3); + c4append_((val >> (2 * 8)) & full_byte); + c4append_((val >> (1 * 8)) & full_byte); + } + return wpos; + #undef c4append_ + #undef c4appendval_ +} + +} // namespace c4 + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/base64.hpp b/thirdparty/ryml/ext/c4core/src/c4/base64.hpp new file mode 100644 index 000000000..673f4d77e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/base64.hpp @@ -0,0 +1,98 @@ +#ifndef _C4_BASE64_HPP_ +#define _C4_BASE64_HPP_ + +/** @file base64.hpp encoding/decoding for base64. + * @see https://en.wikipedia.org/wiki/Base64 + * @see https://www.base64encode.org/ + * */ + +#include "c4/charconv.hpp" +#include "c4/blob.hpp" + +namespace c4 { + +/** check that the given buffer is a valid base64 encoding + * @see https://en.wikipedia.org/wiki/Base64 */ +bool base64_valid(csubstr encoded); + +/** base64-encode binary data. + * @param encoded [out] output buffer for encoded data + * @param data [in] the input buffer with the binary data + * @return the number of bytes needed to return the output. No writes occur beyond the end of the output buffer. + * @see https://en.wikipedia.org/wiki/Base64 */ +size_t base64_encode(substr encoded, cblob data); + +/** decode the base64 encoding in the given buffer + * @param encoded [in] the encoded base64 + * @param data [out] the output buffer + * @return the number of bytes needed to return the output.. No writes occur beyond the end of the output buffer. + * @see https://en.wikipedia.org/wiki/Base64 */ +size_t base64_decode(csubstr encoded, blob data); + + +namespace fmt { + +template<typename CharOrConstChar> +struct base64_wrapper_ +{ + blob_<CharOrConstChar> data; + base64_wrapper_() : data() {} + base64_wrapper_(blob_<CharOrConstChar> blob) : data(blob) {} +}; +using const_base64_wrapper = base64_wrapper_<cbyte>; +using base64_wrapper = base64_wrapper_<byte>; + + +/** mark a variable to be written in base64 format */ +template<class ...Args> +C4_ALWAYS_INLINE const_base64_wrapper cbase64(Args const& C4_RESTRICT ...args) +{ + return const_base64_wrapper(cblob(args...)); +} +/** mark a csubstr to be written in base64 format */ +C4_ALWAYS_INLINE const_base64_wrapper cbase64(csubstr s) +{ + return const_base64_wrapper(cblob(s.str, s.len)); +} +/** mark a variable to be written in base64 format */ +template<class ...Args> +C4_ALWAYS_INLINE const_base64_wrapper base64(Args const& C4_RESTRICT ...args) +{ + return const_base64_wrapper(cblob(args...)); +} +/** mark a csubstr to be written in base64 format */ +C4_ALWAYS_INLINE const_base64_wrapper base64(csubstr s) +{ + return const_base64_wrapper(cblob(s.str, s.len)); +} + +/** mark a variable to be read in base64 format */ +template<class ...Args> +C4_ALWAYS_INLINE base64_wrapper base64(Args &... args) +{ + return base64_wrapper(blob(args...)); +} +/** mark a variable to be read in base64 format */ +C4_ALWAYS_INLINE base64_wrapper base64(substr s) +{ + return base64_wrapper(blob(s.str, s.len)); +} + +} // namespace fmt + + +/** write a variable in base64 format */ +inline size_t to_chars(substr buf, fmt::const_base64_wrapper b) +{ + return base64_encode(buf, b.data); +} + +/** read a variable in base64 format */ +inline size_t from_chars(csubstr buf, fmt::base64_wrapper *b) +{ + return base64_decode(buf, b->data); +} + +} // namespace c4 + +#endif /* _C4_BASE64_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/bitmask.hpp b/thirdparty/ryml/ext/c4core/src/c4/bitmask.hpp new file mode 100644 index 000000000..8c239da30 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/bitmask.hpp @@ -0,0 +1,330 @@ +#ifndef _C4_BITMASK_HPP_ +#define _C4_BITMASK_HPP_ + +/** @file bitmask.hpp bitmask utilities */ + +#include <cstring> +#include <type_traits> + +#include "c4/enum.hpp" +#include "c4/format.hpp" + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4996) // 'strncpy', fopen, etc: This function or variable may be unsafe +#elif defined(__clang__) +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# if __GNUC__ >= 8 +# pragma GCC diagnostic ignored "-Wstringop-truncation" +# pragma GCC diagnostic ignored "-Wstringop-overflow" +# endif +#endif + +namespace c4 { + +//----------------------------------------------------------------------------- +/** write a bitmask to a stream, formatted as a string */ + +template<class Enum, class Stream> +Stream& bm2stream(Stream &s, typename std::underlying_type<Enum>::type bits, EnumOffsetType offst=EOFFS_PFX) +{ + using I = typename std::underlying_type<Enum>::type; + bool written = false; + + auto const& pairs = esyms<Enum>(); + + // write non null value + if(bits) + { + // do reverse iteration to give preference to composite enum symbols, + // which are likely to appear at the end of the enum sequence + for(size_t i = pairs.size() - 1; i != size_t(-1); --i) + { + auto p = pairs[i]; + I b(static_cast<I>(p.value)); + if(b && (bits & b) == b) + { + if(written) s << '|'; // append bit-or character + written = true; + s << p.name_offs(offst); // append bit string + bits &= ~b; + } + } + return s; + } + else + { + // write a null value + for(size_t i = pairs.size() - 1; i != size_t(-1); --i) + { + auto p = pairs[i]; + I b(static_cast<I>(p.value)); + if(b == 0) + { + s << p.name_offs(offst); + written = true; + break; + } + } + } + if(!written) + { + s << '0'; + } + return s; +} + +template<class Enum, class Stream> +typename std::enable_if<is_scoped_enum<Enum>::value, Stream&>::type +bm2stream(Stream &s, Enum value, EnumOffsetType offst=EOFFS_PFX) +{ + using I = typename std::underlying_type<Enum>::type; + return bm2stream<Enum>(s, static_cast<I>(value), offst); +} + + +//----------------------------------------------------------------------------- + +// some utility macros, undefed below + +/// @cond dev + +/* Execute `code` if the `num` of characters is available in the str + * buffer. This macro simplifies the code for bm2str(). + * @todo improve performance by writing from the end and moving only once. */ +#define _c4prependchars(code, num) \ + if(str && (pos + num <= sz)) \ + { \ + /* move the current string to the right */ \ + memmove(str + num, str, pos); \ + /* now write in the beginning of the string */ \ + code; \ + } \ + else if(str && sz) \ + { \ + C4_ERROR("cannot write to string pos=%d num=%d sz=%d", \ + (int)pos, (int)num, (int)sz); \ + } \ + pos += num + +/* Execute `code` if the `num` of characters is available in the str + * buffer. This macro simplifies the code for bm2str(). */ +#define _c4appendchars(code, num) \ + if(str && (pos + num <= sz)) \ + { \ + code; \ + } \ + else if(str && sz) \ + { \ + C4_ERROR("cannot write to string pos=%d num=%d sz=%d", \ + (int)pos, (int)num, (int)sz); \ + } \ + pos += num + +/// @endcond + + +/** convert a bitmask to string. + * return the number of characters written. To find the needed size, + * call first with str=nullptr and sz=0 */ +template<class Enum> +size_t bm2str +( + typename std::underlying_type<Enum>::type bits, + char *str=nullptr, + size_t sz=0, + EnumOffsetType offst=EOFFS_PFX +) +{ + using I = typename std::underlying_type<Enum>::type; + C4_ASSERT((str == nullptr) == (sz == 0)); + + auto syms = esyms<Enum>(); + size_t pos = 0; + typename EnumSymbols<Enum>::Sym const* C4_RESTRICT zero = nullptr; + + // do reverse iteration to give preference to composite enum symbols, + // which are likely to appear later in the enum sequence + for(size_t i = syms.size()-1; i != size_t(-1); --i) + { + auto const &C4_RESTRICT p = syms[i]; // do not copy, we are assigning to `zero` + I b = static_cast<I>(p.value); + if(b == 0) + { + zero = &p; // save this symbol for later + } + else if((bits & b) == b) + { + bits &= ~b; + // append bit-or character + if(pos > 0) + { + _c4prependchars(*str = '|', 1); + } + // append bit string + const char *pname = p.name_offs(offst); + size_t len = strlen(pname); + _c4prependchars(strncpy(str, pname, len), len); + } + } + + C4_CHECK_MSG(bits == 0, "could not find all bits"); + if(pos == 0) // make sure at least something is written + { + if(zero) // if we have a zero symbol, use that + { + const char *pname = zero->name_offs(offst); + size_t len = strlen(pname); + _c4prependchars(strncpy(str, pname, len), len); + } + else // otherwise just write an integer zero + { + _c4prependchars(*str = '0', 1); + } + } + _c4appendchars(str[pos] = '\0', 1); + + return pos; +} + + +// cleanup! +#undef _c4appendchars +#undef _c4prependchars + + +/** scoped enums do not convert automatically to their underlying type, + * so this SFINAE overload will accept scoped enum symbols and cast them + * to the underlying type */ +template<class Enum> +typename std::enable_if<is_scoped_enum<Enum>::value, size_t>::type +bm2str +( + Enum bits, + char *str=nullptr, + size_t sz=0, + EnumOffsetType offst=EOFFS_PFX +) +{ + using I = typename std::underlying_type<Enum>::type; + return bm2str<Enum>(static_cast<I>(bits), str, sz, offst); +} + + +//----------------------------------------------------------------------------- + +namespace detail { + +#ifdef __clang__ +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# if __GNUC__ >= 6 +# pragma GCC diagnostic ignored "-Wnull-dereference" +# endif +#endif + +template<class Enum> +typename std::underlying_type<Enum>::type str2bm_read_one(const char *str, size_t sz, bool alnum) +{ + using I = typename std::underlying_type<Enum>::type; + auto pairs = esyms<Enum>(); + if(alnum) + { + auto *p = pairs.find(str, sz); + C4_CHECK_MSG(p != nullptr, "no valid enum pair name for '%.*s'", (int)sz, str); + return static_cast<I>(p->value); + } + I tmp; + size_t len = uncat(csubstr(str, sz), tmp); + C4_CHECK_MSG(len != csubstr::npos, "could not read string as an integral type: '%.*s'", (int)sz, str); + return tmp; +} + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +} // namespace detail + +/** convert a string to a bitmask */ +template<class Enum> +typename std::underlying_type<Enum>::type str2bm(const char *str, size_t sz) +{ + using I = typename std::underlying_type<Enum>::type; + + I val = 0; + bool started = false; + bool alnum = false, num = false; + const char *f = nullptr, *pc = str; + for( ; pc < str+sz; ++pc) + { + const char c = *pc; + if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_') + { + C4_CHECK(( ! num) || ((pc - f) == 1 && (c == 'x' || c == 'X'))); // accept hexadecimal numbers + if( ! started) + { + f = pc; + alnum = started = true; + } + } + else if(c >= '0' && c <= '9') + { + C4_CHECK( ! alnum); + if(!started) + { + f = pc; + num = started = true; + } + } + else if(c == ':' || c == ' ') + { + // skip this char + } + else if(c == '|' || c == '\0') + { + C4_ASSERT(num != alnum); + C4_ASSERT(pc >= f); + val |= detail::str2bm_read_one<Enum>(f, static_cast<size_t>(pc-f), alnum); + started = num = alnum = false; + if(c == '\0') + { + return val; + } + } + else + { + C4_ERROR("bad character '%c' in bitmask string", c); + } + } + + if(f) + { + C4_ASSERT(num != alnum); + C4_ASSERT(pc >= f); + val |= detail::str2bm_read_one<Enum>(f, static_cast<size_t>(pc-f), alnum); + } + + return val; +} + +/** convert a string to a bitmask */ +template<class Enum> +typename std::underlying_type<Enum>::type str2bm(const char *str) +{ + return str2bm<Enum>(str, strlen(str)); +} + +} // namespace c4 + +#ifdef _MSC_VER +# pragma warning(pop) +#elif defined(__clang__) +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#endif // _C4_BITMASK_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/blob.hpp b/thirdparty/ryml/ext/c4core/src/c4/blob.hpp new file mode 100644 index 000000000..9c233d9f3 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/blob.hpp @@ -0,0 +1,50 @@ +#ifndef _C4_BLOB_HPP_ +#define _C4_BLOB_HPP_ + +#include "c4/types.hpp" +#include "c4/error.hpp" + +/** @file blob.hpp Mutable and immutable binary data blobs. +*/ + +namespace c4 { + +template<class T> +struct blob_ +{ + T * buf; + size_t len; + + C4_ALWAYS_INLINE blob_() noexcept : buf(), len() {} + + C4_ALWAYS_INLINE blob_(blob_ const& that) noexcept = default; + C4_ALWAYS_INLINE blob_(blob_ && that) noexcept = default; + C4_ALWAYS_INLINE blob_& operator=(blob_ && that) noexcept = default; + C4_ALWAYS_INLINE blob_& operator=(blob_ const& that) noexcept = default; + + // need to sfinae out copy constructors! (why? isn't the above sufficient?) + #define _C4_REQUIRE_NOT_SAME class=typename std::enable_if<( ! std::is_same<U, blob_>::value) && ( ! std::is_pointer<U>::value), T>::type + template<class U, _C4_REQUIRE_NOT_SAME> C4_ALWAYS_INLINE blob_(U &var) noexcept : buf(reinterpret_cast<T*>(&var)), len(sizeof(U)) {} + template<class U, _C4_REQUIRE_NOT_SAME> C4_ALWAYS_INLINE blob_& operator= (U &var) noexcept { buf = reinterpret_cast<T*>(&var); len = sizeof(U); return *this; } + #undef _C4_REQUIRE_NOT_SAME + + template<class U, size_t N> C4_ALWAYS_INLINE blob_(U (&arr)[N]) noexcept : buf(reinterpret_cast<T*>(arr)), len(sizeof(U) * N) {} + template<class U, size_t N> C4_ALWAYS_INLINE blob_& operator= (U (&arr)[N]) noexcept { buf = reinterpret_cast<T*>(arr); len = sizeof(U) * N; return *this; } + + template<class U> + C4_ALWAYS_INLINE blob_(U *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(sizeof(U) * n) { C4_ASSERT(is_aligned(ptr)); } + C4_ALWAYS_INLINE blob_(void *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {} + C4_ALWAYS_INLINE blob_(void const *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {} +}; + +/** an immutable binary blob */ +using cblob = blob_<cbyte>; +/** a mutable binary blob */ +using blob = blob_< byte>; + +C4_MUST_BE_TRIVIAL_COPY(blob); +C4_MUST_BE_TRIVIAL_COPY(cblob); + +} // namespace c4 + +#endif // _C4_BLOB_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/c4_pop.hpp b/thirdparty/ryml/ext/c4core/src/c4/c4_pop.hpp new file mode 100644 index 000000000..3aa213352 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/c4_pop.hpp @@ -0,0 +1,19 @@ +#ifdef _C4_PUSH_HPP_ // this must match the include guard from c4_push + +/** @file c4_pop.hpp disables the macros and control directives + * enabled in c4_push.hpp. + * @see c4_push.hpp */ + +#include "c4/unrestrict.hpp" + +#ifdef C4_WIN +# include "c4/windows_pop.hpp" +#endif + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#undef _C4_PUSH_HPP_ + +#endif /* _C4_PUSH_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/c4_push.hpp b/thirdparty/ryml/ext/c4core/src/c4/c4_push.hpp new file mode 100644 index 000000000..5498bdb28 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/c4_push.hpp @@ -0,0 +1,37 @@ +#ifndef _C4_PUSH_HPP_ +#define _C4_PUSH_HPP_ + + +/** @file c4_push.hpp enables macros and warning control directives + * needed by c4core. This is implemented in a push/pop way. + * @see c4_pop.hpp */ + + +#ifndef _C4_CONFIG_HPP_ +#include "c4/config.hpp" +#endif + +#include "c4/restrict.hpp" + +#ifdef C4_WIN +# include "c4/windows_push.hpp" +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4068) // unknown pragma +# pragma warning(disable : 4100) // unreferenced formal parameter +# pragma warning(disable : 4127) // conditional expression is constant -- eg do {} while(1); +# pragma warning(disable : 4201) // nonstandard extension used : nameless struct/union +//# pragma warning(disable : 4238) // nonstandard extension used: class rvalue used as lvalue +# pragma warning(disable : 4244) +# pragma warning(disable : 4503) // decorated name length exceeded, name was truncated +# pragma warning(disable : 4702) // unreachable code +# pragma warning(disable : 4714) // function marked as __forceinline not inlined +# pragma warning(disable : 4996) // 'strncpy', fopen, etc: This function or variable may be unsafe +# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017 +# pragma warning(disable : 4800) // forcing value to bool 'true' or 'false' (performance warning) +# endif +#endif + +#endif /* _C4_PUSH_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/c4core.natvis b/thirdparty/ryml/ext/c4core/src/c4/c4core.natvis new file mode 100644 index 000000000..7cd138fe6 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/c4core.natvis @@ -0,0 +1,168 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- +Very good intro: +@see https://code.msdn.microsoft.com/windowsdesktop/Writing-type-visualizers-2eae77a2 +See also: +@see http://blogs.msdn.com/b/vcblog/archive/2013/06/28/using-visual-studio-2013-to-write-maintainable-native-visualizations-natvis.aspx?PageIndex=2 +@see http://blogs.msdn.com/b/vcblog/archive/2015/09/28/debug-visualizers-in-visual-c-2015.aspx +@see http://stackoverflow.com/questions/36883414/limit-display-of-char-in-natvis-file-to-specific-length +--> + +<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> + + <Type Name="c4::basic_substring<*>"> + <DisplayString>{str,[len]} (sz={len})</DisplayString> + <StringView>str,[len]</StringView> + <Expand> + <Item Name="[size]">len</Item> + <ArrayItems> + <Size>len</Size> + <ValuePointer>str</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <Type Name="c4::span<*>"> + <DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString> + <Expand> + <Item Name="[size]">m_size</Item> + <ArrayItems> + <Size>m_size</Size> + <ValuePointer>m_ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + <Type Name="c4::spanrs<*>"> + <DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString> + <Expand> + <Item Name="[size]">m_size</Item> + <Item Name="[capacity]">m_capacity</Item> + <ArrayItems> + <Size>m_size</Size> + <ValuePointer>m_ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + <!-- display span<char>/span<const char> as a string too --> + <Type Name="c4::span<char,*>"> + <DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString> + <StringView>m_ptr,[m_size]</StringView> + <Expand> + <Item Name="[size]">m_size</Item> + <ArrayItems> + <Size>m_size</Size> + <ValuePointer>m_ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + <Type Name="c4::span<const char,*>"> + <DisplayString>{m_ptr,[m_size]} (sz={m_size})</DisplayString> + <StringView>m_ptr,[m_size]</StringView> + <Expand> + <Item Name="[size]">m_size</Item> + <ArrayItems> + <Size>m_size</Size> + <ValuePointer>m_ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + <!-- display spanrs<char>/spanrs<const char> as a string too --> + <Type Name="c4::spanrs<char,*>"> + <DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString> + <StringView>m_ptr,[m_size]</StringView> + <Expand> + <Item Name="[size]">m_size</Item> + <Item Name="[capacity]">m_capacity</Item> + <ArrayItems> + <Size>m_size</Size> + <ValuePointer>m_ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + <Type Name="c4::spanrs<const char,*>"> + <DisplayString>{m_ptr,[m_size]} (sz={m_size}, cap={m_capacity})</DisplayString> + <StringView>m_ptr,[m_size]</StringView> + <Expand> + <Item Name="[size]">m_size</Item> + <Item Name="[capacity]">m_capacity</Item> + <ArrayItems> + <Size>m_size</Size> + <ValuePointer>m_ptr</ValuePointer> + </ArrayItems> + </Expand> + </Type> + + <!-- =========================================================================================== --> + <Type Name="c4::string_impl<*,*,*,*>"> + <DisplayString>{(($T3*)this)->m_str,[(($T3*)this)->m_size]} (sz={(($T3*)this)->m_size})</DisplayString> + <StringView>(($T3*)this)->m_str,[(($T3*)this)->m_size]</StringView> + <Expand> + <Synthetic Name="m_str"> + <DisplayString>{(($T3*)this)->m_str,[(($T3*)this)->m_size]}</DisplayString> + <StringView>(($T3*)this)->m_str,[(($T3*)this)->m_size]</StringView> + </Synthetic> + <Synthetic Name="m_size"> + <DisplayString>{(($T3*)this)->m_size}</DisplayString> + </Synthetic> + </Expand> + </Type> + <Type Name="c4::basic_substring<*,*>"> + <DisplayString>{m_str,[m_size]} (sz={m_size})</DisplayString> + <StringView>m_str,[m_size]</StringView> + <Expand> + <Synthetic Name="[size]"> + <DisplayString>{m_size}</DisplayString> + </Synthetic> + </Expand> + </Type> + <Type Name="c4::basic_substringrs<*,*>"> + <DisplayString>{m_str,[m_size]} (sz={m_size},cap={m_capacity})</DisplayString> + <StringView>m_str,[m_size]</StringView> + <Expand> + <Synthetic Name="[size]"> + <DisplayString>{m_size}</DisplayString> + </Synthetic> + <Synthetic Name="[capacity]"> + <DisplayString>{m_capacity}</DisplayString> + </Synthetic> + <Synthetic Name="[full]"> + <DisplayString>{m_str,[m_capacity]}</DisplayString> + <StringView>m_str,[m_capacity]</StringView> + </Synthetic> + </Expand> + </Type> + <Type Name="c4::basic_string<*,*,*>"> + <DisplayString>{m_str,[m_size]} (sz={m_size},cap={m_capacity})</DisplayString> + <StringView>m_str,[m_size]</StringView> + <Expand> + <Synthetic Name="[size]"> + <DisplayString>{m_size}</DisplayString> + </Synthetic> + <Synthetic Name="[full]"> + <DisplayString>{m_str,[m_capacity]}</DisplayString> + <StringView>m_str,[m_capacity]</StringView> + </Synthetic> + </Expand> + </Type> + + <!-- enum symbols --> + <Type Name="c4::EnumSymbols<*>::Sym"> + <DisplayString>{value} - {name}</DisplayString> + <Expand> + <Item Name="[value]">value</Item> + <Item Name="[name]">name</Item> + </Expand> + </Type> + <Type Name="c4::EnumSymbols<*>"> + <DisplayString>{m_symbols,[m_num]} (sz={m_num})</DisplayString> + <Expand> + <Item Name="[size]">m_num</Item> + <ArrayItems> + <Size>m_num</Size> + <ValuePointer>m_symbols</ValuePointer> + </ArrayItems> + </Expand> + </Type> + +</AutoVisualizer> diff --git a/thirdparty/ryml/ext/c4core/src/c4/char_traits.cpp b/thirdparty/ryml/ext/c4core/src/c4/char_traits.cpp new file mode 100644 index 000000000..053a7c3b1 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/char_traits.cpp @@ -0,0 +1,10 @@ +#include "c4/char_traits.hpp" + +namespace c4 { + +constexpr const char char_traits< char >::whitespace_chars[]; +constexpr const size_t char_traits< char >::num_whitespace_chars; +constexpr const wchar_t char_traits< wchar_t >::whitespace_chars[]; +constexpr const size_t char_traits< wchar_t >::num_whitespace_chars; + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/src/c4/char_traits.hpp b/thirdparty/ryml/ext/c4core/src/c4/char_traits.hpp new file mode 100644 index 000000000..b080659fb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/char_traits.hpp @@ -0,0 +1,98 @@ +#ifndef _C4_CHAR_TRAITS_HPP_ +#define _C4_CHAR_TRAITS_HPP_ + +#include "c4/config.hpp" + +#include <string> // needed because of std::char_traits +#include <cctype> +#include <cwctype> + +namespace c4 { + +C4_ALWAYS_INLINE bool isspace(char c) { return std::isspace(c) != 0; } +C4_ALWAYS_INLINE bool isspace(wchar_t c) { return std::iswspace(static_cast<wint_t>(c)) != 0; } + +//----------------------------------------------------------------------------- +template<typename C> +struct char_traits; + +template<> +struct char_traits<char> : public std::char_traits<char> +{ + constexpr static const char whitespace_chars[] = " \f\n\r\t\v"; + constexpr static const size_t num_whitespace_chars = sizeof(whitespace_chars) - 1; +}; + +template<> +struct char_traits<wchar_t> : public std::char_traits<wchar_t> +{ + constexpr static const wchar_t whitespace_chars[] = L" \f\n\r\t\v"; + constexpr static const size_t num_whitespace_chars = sizeof(whitespace_chars) - 1; +}; + + +//----------------------------------------------------------------------------- +namespace detail { +template<typename C> +struct needed_chars; +template<> +struct needed_chars<char> +{ + template<class SizeType> + C4_ALWAYS_INLINE constexpr static SizeType for_bytes(SizeType num_bytes) + { + return num_bytes; + } +}; +template<> +struct needed_chars<wchar_t> +{ + template<class SizeType> + C4_ALWAYS_INLINE constexpr static SizeType for_bytes(SizeType num_bytes) + { + // wchar_t is not necessarily 2 bytes. + return (num_bytes / static_cast<SizeType>(sizeof(wchar_t))) + ((num_bytes & static_cast<SizeType>(SizeType(sizeof(wchar_t)) - SizeType(1))) != 0); + } +}; +} // namespace detail + +/** get the number of C characters needed to store a number of bytes */ +template<typename C, typename SizeType> +C4_ALWAYS_INLINE constexpr SizeType num_needed_chars(SizeType num_bytes) +{ + return detail::needed_chars<C>::for_bytes(num_bytes); +} + + +//----------------------------------------------------------------------------- + +/** get the given text string as either char or wchar_t according to the given type */ +#define C4_TXTTY(txt, type) \ + /* is there a smarter way to do this? */\ + c4::detail::literal_as<type>::get(txt, C4_WIDEN(txt)) + +namespace detail { +template<typename C> +struct literal_as; + +template<> +struct literal_as<char> +{ + C4_ALWAYS_INLINE static constexpr const char* get(const char* str, const wchar_t *) + { + return str; + } +}; +template<> +struct literal_as<wchar_t> +{ + C4_ALWAYS_INLINE static constexpr const wchar_t* get(const char*, const wchar_t *wstr) + { + return wstr; + } +}; +} // namespace detail + +} // namespace c4 + +#endif /* _C4_CHAR_TRAITS_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/charconv.hpp b/thirdparty/ryml/ext/c4core/src/c4/charconv.hpp new file mode 100644 index 000000000..af44f136c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/charconv.hpp @@ -0,0 +1,2407 @@ +#ifndef _C4_CHARCONV_HPP_ +#define _C4_CHARCONV_HPP_ + +/** @file charconv.hpp Lightweight generic type-safe wrappers for + * converting individual values to/from strings. + * + * These are the main functions: + * + * @code{.cpp} + * // Convert the given value, writing into the string. + * // The resulting string will NOT be null-terminated. + * // Return the number of characters needed. + * // This function is safe to call when the string is too small - + * // no writes will occur beyond the string's last character. + * template<class T> size_t c4::to_chars(substr buf, T const& C4_RESTRICT val); + * + * + * // Convert the given value to a string using to_chars(), and + * // return the resulting string, up to and including the last + * // written character. + * template<class T> substr c4::to_chars_sub(substr buf, T const& C4_RESTRICT val); + * + * + * // Read a value from the string, which must be + * // trimmed to the value (ie, no leading/trailing whitespace). + * // return true if the conversion succeeded. + * // There is no check for overflow; the value wraps around in a way similar + * // to the standard C/C++ overflow behavior. For example, + * // from_chars<int8_t>("128", &val) returns true and val will be + * // set tot 0. + * template<class T> bool c4::from_chars(csubstr buf, T * C4_RESTRICT val); + * + * + * // Read the first valid sequence of characters from the string, + * // skipping leading whitespace, and convert it using from_chars(). + * // Return the number of characters read for converting. + * template<class T> size_t c4::from_chars_first(csubstr buf, T * C4_RESTRICT val); + * @endcode + */ + +#include "c4/language.hpp" +#include <inttypes.h> +#include <type_traits> +#include <climits> +#include <limits> +#include <utility> + +#include "c4/config.hpp" +#include "c4/substr.hpp" +#include "c4/std/std_fwd.hpp" +#include "c4/memory_util.hpp" +#include "c4/szconv.hpp" + +#ifndef C4CORE_NO_FAST_FLOAT +# if (C4_CPP >= 17) +# if defined(_MSC_VER) +# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) // VS2017 and lower do not have these macros +# include <charconv> +# define C4CORE_HAVE_STD_TOCHARS 1 +# define C4CORE_HAVE_STD_FROMCHARS 0 // prefer fast_float with MSVC +# define C4CORE_HAVE_FAST_FLOAT 1 +# else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 1 +# endif +# else +# if __has_include(<charconv>) +# include <charconv> +# if defined(__cpp_lib_to_chars) +# define C4CORE_HAVE_STD_TOCHARS 1 +# define C4CORE_HAVE_STD_FROMCHARS 0 // glibc uses fast_float internally +# define C4CORE_HAVE_FAST_FLOAT 1 +# else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 1 +# endif +# else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 1 +# endif +# endif +# else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 1 +# endif +# if C4CORE_HAVE_FAST_FLOAT + C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wsign-conversion") + C4_SUPPRESS_WARNING_GCC("-Warray-bounds") +# if __GNUC__ >= 5 + C4_SUPPRESS_WARNING_GCC("-Wshift-count-overflow") +# endif +# include "c4/ext/fast_float.hpp" + C4_SUPPRESS_WARNING_GCC_POP +# endif +#elif (C4_CPP >= 17) +# define C4CORE_HAVE_FAST_FLOAT 0 +# if defined(_MSC_VER) +# if (C4_MSVC_VERSION >= C4_MSVC_VERSION_2019) // VS2017 and lower do not have these macros +# include <charconv> +# define C4CORE_HAVE_STD_TOCHARS 1 +# define C4CORE_HAVE_STD_FROMCHARS 1 +# else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# endif +# else +# if __has_include(<charconv>) +# include <charconv> +# if defined(__cpp_lib_to_chars) +# define C4CORE_HAVE_STD_TOCHARS 1 +# define C4CORE_HAVE_STD_FROMCHARS 1 // glibc uses fast_float internally +# else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# endif +# else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# endif +# endif +#else +# define C4CORE_HAVE_STD_TOCHARS 0 +# define C4CORE_HAVE_STD_FROMCHARS 0 +# define C4CORE_HAVE_FAST_FLOAT 0 +#endif + + +#if !C4CORE_HAVE_STD_FROMCHARS +#include <cstdio> +#endif + + +#ifdef _MSC_VER +# pragma warning(push) +# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017 +# pragma warning(disable: 4800) //'int': forcing value to bool 'true' or 'false' (performance warning) +# endif +# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe +#elif defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +# pragma clang diagnostic ignored "-Wformat-nonliteral" +# pragma clang diagnostic ignored "-Wdouble-promotion" // implicit conversion increases floating-point precision +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +# pragma GCC diagnostic ignored "-Wdouble-promotion" // implicit conversion increases floating-point precision +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif + + +namespace c4 { + +#if C4CORE_HAVE_STD_TOCHARS +/** @warning Use only the symbol. Do not rely on the type or naked value of this enum. */ +typedef enum : std::underlying_type<std::chars_format>::type { + /** print the real number in floating point format (like %f) */ + FTOA_FLOAT = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::fixed), + /** print the real number in scientific format (like %e) */ + FTOA_SCIENT = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::scientific), + /** print the real number in flexible format (like %g) */ + FTOA_FLEX = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::general), + /** print the real number in hexadecimal format (like %a) */ + FTOA_HEXA = static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::hex), +} RealFormat_e; +#else +/** @warning Use only the symbol. Do not rely on the type or naked value of this enum. */ +typedef enum : char { + /** print the real number in floating point format (like %f) */ + FTOA_FLOAT = 'f', + /** print the real number in scientific format (like %e) */ + FTOA_SCIENT = 'e', + /** print the real number in flexible format (like %g) */ + FTOA_FLEX = 'g', + /** print the real number in hexadecimal format (like %a) */ + FTOA_HEXA = 'a', +} RealFormat_e; +#endif + + +/** in some platforms, int,unsigned int + * are not any of int8_t...int64_t and + * long,unsigned long are not any of uint8_t...uint64_t */ +template<class T> +struct is_fixed_length +{ + enum : bool { + /** true if T is one of the fixed length signed types */ + value_i = (std::is_integral<T>::value + && (std::is_same<T, int8_t>::value + || std::is_same<T, int16_t>::value + || std::is_same<T, int32_t>::value + || std::is_same<T, int64_t>::value)), + /** true if T is one of the fixed length unsigned types */ + value_u = (std::is_integral<T>::value + && (std::is_same<T, uint8_t>::value + || std::is_same<T, uint16_t>::value + || std::is_same<T, uint32_t>::value + || std::is_same<T, uint64_t>::value)), + /** true if T is one of the fixed length signed or unsigned types */ + value = value_i || value_u + }; +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +#ifdef _MSC_VER +# pragma warning(push) +#elif defined(__clang__) +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +# if __GNUC__ >= 6 +# pragma GCC diagnostic ignored "-Wnull-dereference" +# endif +#endif + +namespace detail { + +/* python command to get the values below: +def dec(v): + return str(v) +for bits in (8, 16, 32, 64): + imin, imax, umax = (-(1 << (bits - 1))), (1 << (bits - 1)) - 1, (1 << bits) - 1 + for vname, v in (("imin", imin), ("imax", imax), ("umax", umax)): + for f in (bin, oct, dec, hex): + print(f"{bits}b: {vname}={v} {f.__name__}: len={len(f(v)):2d}: {v} {f(v)}") +*/ + +// do not use the type as the template argument because in some +// platforms long!=int32 and long!=int64. Just use the numbytes +// which is more generic and spares lengthy SFINAE code. +template<size_t num_bytes, bool is_signed> struct charconv_digits_; +template<class T> using charconv_digits = charconv_digits_<sizeof(T), std::is_signed<T>::value>; + +template<> struct charconv_digits_<1u, true> // int8_t +{ + enum : size_t { + maxdigits_bin = 1 + 2 + 8, // -128==-0b10000000 + maxdigits_oct = 1 + 2 + 3, // -128==-0o200 + maxdigits_dec = 1 + 3, // -128 + maxdigits_hex = 1 + 2 + 2, // -128==-0x80 + maxdigits_bin_nopfx = 8, // -128==-0b10000000 + maxdigits_oct_nopfx = 3, // -128==-0o200 + maxdigits_dec_nopfx = 3, // -128 + maxdigits_hex_nopfx = 2, // -128==-0x80 + }; + // min values without sign! + static constexpr csubstr min_value_dec() noexcept { return csubstr("128"); } + static constexpr csubstr min_value_hex() noexcept { return csubstr("80"); } + static constexpr csubstr min_value_oct() noexcept { return csubstr("200"); } + static constexpr csubstr min_value_bin() noexcept { return csubstr("10000000"); } + static constexpr csubstr max_value_dec() noexcept { return csubstr("127"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 3) || (str.len == 3 && str[0] <= '1')); } +}; +template<> struct charconv_digits_<1u, false> // uint8_t +{ + enum : size_t { + maxdigits_bin = 2 + 8, // 255 0b11111111 + maxdigits_oct = 2 + 3, // 255 0o377 + maxdigits_dec = 3, // 255 + maxdigits_hex = 2 + 2, // 255 0xff + maxdigits_bin_nopfx = 8, // 255 0b11111111 + maxdigits_oct_nopfx = 3, // 255 0o377 + maxdigits_dec_nopfx = 3, // 255 + maxdigits_hex_nopfx = 2, // 255 0xff + }; + static constexpr csubstr max_value_dec() noexcept { return csubstr("255"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 3) || (str.len == 3 && str[0] <= '3')); } +}; +template<> struct charconv_digits_<2u, true> // int16_t +{ + enum : size_t { + maxdigits_bin = 1 + 2 + 16, // -32768 -0b1000000000000000 + maxdigits_oct = 1 + 2 + 6, // -32768 -0o100000 + maxdigits_dec = 1 + 5, // -32768 -32768 + maxdigits_hex = 1 + 2 + 4, // -32768 -0x8000 + maxdigits_bin_nopfx = 16, // -32768 -0b1000000000000000 + maxdigits_oct_nopfx = 6, // -32768 -0o100000 + maxdigits_dec_nopfx = 5, // -32768 -32768 + maxdigits_hex_nopfx = 4, // -32768 -0x8000 + }; + // min values without sign! + static constexpr csubstr min_value_dec() noexcept { return csubstr("32768"); } + static constexpr csubstr min_value_hex() noexcept { return csubstr("8000"); } + static constexpr csubstr min_value_oct() noexcept { return csubstr("100000"); } + static constexpr csubstr min_value_bin() noexcept { return csubstr("1000000000000000"); } + static constexpr csubstr max_value_dec() noexcept { return csubstr("32767"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 6)); } +}; +template<> struct charconv_digits_<2u, false> // uint16_t +{ + enum : size_t { + maxdigits_bin = 2 + 16, // 65535 0b1111111111111111 + maxdigits_oct = 2 + 6, // 65535 0o177777 + maxdigits_dec = 6, // 65535 65535 + maxdigits_hex = 2 + 4, // 65535 0xffff + maxdigits_bin_nopfx = 16, // 65535 0b1111111111111111 + maxdigits_oct_nopfx = 6, // 65535 0o177777 + maxdigits_dec_nopfx = 6, // 65535 65535 + maxdigits_hex_nopfx = 4, // 65535 0xffff + }; + static constexpr csubstr max_value_dec() noexcept { return csubstr("65535"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 6) || (str.len == 6 && str[0] <= '1')); } +}; +template<> struct charconv_digits_<4u, true> // int32_t +{ + enum : size_t { + maxdigits_bin = 1 + 2 + 32, // len=35: -2147483648 -0b10000000000000000000000000000000 + maxdigits_oct = 1 + 2 + 11, // len=14: -2147483648 -0o20000000000 + maxdigits_dec = 1 + 10, // len=11: -2147483648 -2147483648 + maxdigits_hex = 1 + 2 + 8, // len=11: -2147483648 -0x80000000 + maxdigits_bin_nopfx = 32, // len=35: -2147483648 -0b10000000000000000000000000000000 + maxdigits_oct_nopfx = 11, // len=14: -2147483648 -0o20000000000 + maxdigits_dec_nopfx = 10, // len=11: -2147483648 -2147483648 + maxdigits_hex_nopfx = 8, // len=11: -2147483648 -0x80000000 + }; + // min values without sign! + static constexpr csubstr min_value_dec() noexcept { return csubstr("2147483648"); } + static constexpr csubstr min_value_hex() noexcept { return csubstr("80000000"); } + static constexpr csubstr min_value_oct() noexcept { return csubstr("20000000000"); } + static constexpr csubstr min_value_bin() noexcept { return csubstr("10000000000000000000000000000000"); } + static constexpr csubstr max_value_dec() noexcept { return csubstr("2147483647"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 11) || (str.len == 11 && str[0] <= '1')); } +}; +template<> struct charconv_digits_<4u, false> // uint32_t +{ + enum : size_t { + maxdigits_bin = 2 + 32, // len=34: 4294967295 0b11111111111111111111111111111111 + maxdigits_oct = 2 + 11, // len=13: 4294967295 0o37777777777 + maxdigits_dec = 10, // len=10: 4294967295 4294967295 + maxdigits_hex = 2 + 8, // len=10: 4294967295 0xffffffff + maxdigits_bin_nopfx = 32, // len=34: 4294967295 0b11111111111111111111111111111111 + maxdigits_oct_nopfx = 11, // len=13: 4294967295 0o37777777777 + maxdigits_dec_nopfx = 10, // len=10: 4294967295 4294967295 + maxdigits_hex_nopfx = 8, // len=10: 4294967295 0xffffffff + }; + static constexpr csubstr max_value_dec() noexcept { return csubstr("4294967295"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 11) || (str.len == 11 && str[0] <= '3')); } +}; +template<> struct charconv_digits_<8u, true> // int32_t +{ + enum : size_t { + maxdigits_bin = 1 + 2 + 64, // len=67: -9223372036854775808 -0b1000000000000000000000000000000000000000000000000000000000000000 + maxdigits_oct = 1 + 2 + 22, // len=25: -9223372036854775808 -0o1000000000000000000000 + maxdigits_dec = 1 + 19, // len=20: -9223372036854775808 -9223372036854775808 + maxdigits_hex = 1 + 2 + 16, // len=19: -9223372036854775808 -0x8000000000000000 + maxdigits_bin_nopfx = 64, // len=67: -9223372036854775808 -0b1000000000000000000000000000000000000000000000000000000000000000 + maxdigits_oct_nopfx = 22, // len=25: -9223372036854775808 -0o1000000000000000000000 + maxdigits_dec_nopfx = 19, // len=20: -9223372036854775808 -9223372036854775808 + maxdigits_hex_nopfx = 16, // len=19: -9223372036854775808 -0x8000000000000000 + }; + static constexpr csubstr min_value_dec() noexcept { return csubstr("9223372036854775808"); } + static constexpr csubstr min_value_hex() noexcept { return csubstr("8000000000000000"); } + static constexpr csubstr min_value_oct() noexcept { return csubstr("1000000000000000000000"); } + static constexpr csubstr min_value_bin() noexcept { return csubstr("1000000000000000000000000000000000000000000000000000000000000000"); } + static constexpr csubstr max_value_dec() noexcept { return csubstr("9223372036854775807"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 22)); } +}; +template<> struct charconv_digits_<8u, false> +{ + enum : size_t { + maxdigits_bin = 2 + 64, // len=66: 18446744073709551615 0b1111111111111111111111111111111111111111111111111111111111111111 + maxdigits_oct = 2 + 22, // len=24: 18446744073709551615 0o1777777777777777777777 + maxdigits_dec = 20, // len=20: 18446744073709551615 18446744073709551615 + maxdigits_hex = 2 + 16, // len=18: 18446744073709551615 0xffffffffffffffff + maxdigits_bin_nopfx = 64, // len=66: 18446744073709551615 0b1111111111111111111111111111111111111111111111111111111111111111 + maxdigits_oct_nopfx = 22, // len=24: 18446744073709551615 0o1777777777777777777777 + maxdigits_dec_nopfx = 20, // len=20: 18446744073709551615 18446744073709551615 + maxdigits_hex_nopfx = 16, // len=18: 18446744073709551615 0xffffffffffffffff + }; + static constexpr csubstr max_value_dec() noexcept { return csubstr("18446744073709551615"); } + static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 22) || (str.len == 22 && str[0] <= '1')); } +}; +} // namespace detail + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +// Helper macros, undefined below +#define _c4append(c) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = static_cast<char>(c); } else { ++pos; } } +#define _c4appendhex(i) { if(C4_LIKELY(pos < buf.len)) { buf.str[pos++] = hexchars[i]; } else { ++pos; } } + +/** @name digits_dec return the number of digits required to encode a + * decimal number. + * + * @note At first sight this code may look heavily branchy and + * therefore inefficient. However, measurements revealed this to be + * the fastest among the alternatives. + * + * @see https://github.com/biojppm/c4core/pull/77 */ +/** @{ */ + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec(T v) noexcept + -> typename std::enable_if<sizeof(T) == 1u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return ((v >= 100) ? 3u : ((v >= 10) ? 2u : 1u)); +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec(T v) noexcept + -> typename std::enable_if<sizeof(T) == 2u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return ((v >= 10000) ? 5u : (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec(T v) noexcept + -> typename std::enable_if<sizeof(T) == 4u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return ((v >= 1000000000) ? 10u : (v >= 100000000) ? 9u : (v >= 10000000) ? 8u : + (v >= 1000000) ? 7u : (v >= 100000) ? 6u : (v >= 10000) ? 5u : + (v >= 1000) ? 4u : (v >= 100) ? 3u : (v >= 10) ? 2u : 1u); +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +auto digits_dec(T v) noexcept + -> typename std::enable_if<sizeof(T) == 8u, unsigned>::type +{ + // thanks @fargies!!! + // https://github.com/biojppm/c4core/pull/77#issuecomment-1063753568 + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + if(v >= 1000000000) // 10 + { + if(v >= 100000000000000) // 15 [15-20] range + { + if(v >= 100000000000000000) // 18 (15 + (20 - 15) / 2) + { + if((typename std::make_unsigned<T>::type)v >= 10000000000000000000u) // 20 + return 20u; + else + return (v >= 1000000000000000000) ? 19u : 18u; + } + else if(v >= 10000000000000000) // 17 + return 17u; + else + return(v >= 1000000000000000) ? 16u : 15u; + } + else if(v >= 1000000000000) // 13 + return (v >= 10000000000000) ? 14u : 13u; + else if(v >= 100000000000) // 12 + return 12; + else + return(v >= 10000000000) ? 11u : 10u; + } + else if(v >= 10000) // 5 [5-9] range + { + if(v >= 10000000) // 8 + return (v >= 100000000) ? 9u : 8u; + else if(v >= 1000000) // 7 + return 7; + else + return (v >= 100000) ? 6u : 5u; + } + else if(v >= 100) + return (v >= 1000) ? 4u : 3u; + else + return (v >= 10) ? 2u : 1u; +} + +/** @} */ + + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_hex(T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return v ? 1u + (msb((typename std::make_unsigned<T>::type)v) >> 2u) : 1u; +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_bin(T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + return v ? 1u + msb((typename std::make_unsigned<T>::type)v) : 1u; +} + +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_oct(T v_) noexcept +{ + // TODO: is there a better way? + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v_ >= 0); + using U = typename + std::conditional<sizeof(T) <= sizeof(unsigned), + unsigned, + typename std::make_unsigned<T>::type>::type; + U v = (U) v_; // safe because we require v_ >= 0 + unsigned __n = 1; + const unsigned __b2 = 64u; + const unsigned __b3 = __b2 * 8u; + const unsigned long __b4 = __b3 * 8u; + while(true) + { + if(v < 8u) + return __n; + if(v < __b2) + return __n + 1; + if(v < __b3) + return __n + 2; + if(v < __b4) + return __n + 3; + v /= (U) __b4; + __n += 4; + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace detail { +C4_INLINE_CONSTEXPR const char hexchars[] = "0123456789abcdef"; +C4_INLINE_CONSTEXPR const char digits0099[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; +} // namespace detail + +C4_SUPPRESS_WARNING_GCC_PUSH +C4_SUPPRESS_WARNING_GCC("-Warray-bounds") // gcc has false positives here +#if (defined(__GNUC__) && (__GNUC__ >= 7)) +C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has false positives here +#endif + +template<class T> +C4_HOT C4_ALWAYS_INLINE +void write_dec_unchecked(substr buf, T v, unsigned digits_v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + C4_ASSERT(buf.len >= digits_v); + C4_XASSERT(digits_v == digits_dec(v)); + // in bm_xtoa: checkoncelog_singlediv_write2 + while(v >= T(100)) + { + const T quo = v / T(100); + const auto num = (v - quo * T(100)) << 1u; + v = quo; + buf.str[--digits_v] = detail::digits0099[num + 1]; + buf.str[--digits_v] = detail::digits0099[num]; + } + if(v >= T(10)) + { + C4_ASSERT(digits_v == 2); + const auto num = v << 1u; + buf.str[1] = detail::digits0099[num + 1]; + buf.str[0] = detail::digits0099[num]; + } + else + { + C4_ASSERT(digits_v == 1); + buf.str[0] = (char)('0' + v); + } +} + + +template<class T> +C4_HOT C4_ALWAYS_INLINE +void write_hex_unchecked(substr buf, T v, unsigned digits_v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + C4_ASSERT(buf.len >= digits_v); + C4_XASSERT(digits_v == digits_hex(v)); + do { + buf.str[--digits_v] = detail::hexchars[v & T(15)]; + v >>= 4; + } while(v); + C4_ASSERT(digits_v == 0); +} + + +template<class T> +C4_HOT C4_ALWAYS_INLINE +void write_oct_unchecked(substr buf, T v, unsigned digits_v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + C4_ASSERT(buf.len >= digits_v); + C4_XASSERT(digits_v == digits_oct(v)); + do { + buf.str[--digits_v] = (char)('0' + (v & T(7))); + v >>= 3; + } while(v); + C4_ASSERT(digits_v == 0); +} + + +template<class T> +C4_HOT C4_ALWAYS_INLINE +void write_bin_unchecked(substr buf, T v, unsigned digits_v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + C4_ASSERT(buf.len >= digits_v); + C4_XASSERT(digits_v == digits_bin(v)); + do { + buf.str[--digits_v] = (char)('0' + (v & T(1))); + v >>= 1; + } while(v); + C4_ASSERT(digits_v == 0); +} + + +/** write an integer to a string in decimal format. This is the + * lowest level (and the fastest) function to do this task. + * @note does not accept negative numbers + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the required size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t write_dec(substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + unsigned digits = digits_dec(v); + if(C4_LIKELY(buf.len >= digits)) + write_dec_unchecked(buf, v, digits); + return digits; +} + +/** write an integer to a string in hexadecimal format. This is the + * lowest level (and the fastest) function to do this task. + * @note does not accept negative numbers + * @note does not prefix with 0x + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the required size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t write_hex(substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + unsigned digits = digits_hex(v); + if(C4_LIKELY(buf.len >= digits)) + write_hex_unchecked(buf, v, digits); + return digits; +} + +/** write an integer to a string in octal format. This is the + * lowest level (and the fastest) function to do this task. + * @note does not accept negative numbers + * @note does not prefix with 0o + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the required size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t write_oct(substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + unsigned digits = digits_oct(v); + if(C4_LIKELY(buf.len >= digits)) + write_oct_unchecked(buf, v, digits); + return digits; +} + +/** write an integer to a string in binary format. This is the + * lowest level (and the fastest) function to do this task. + * @note does not accept negative numbers + * @note does not prefix with 0b + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the required size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t write_bin(substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_ASSERT(v >= 0); + unsigned digits = digits_bin(v); + C4_ASSERT(digits > 0); + if(C4_LIKELY(buf.len >= digits)) + write_bin_unchecked(buf, v, digits); + return digits; +} + + +namespace detail { +template<class U> using NumberWriter = size_t (*)(substr, U); +template<class T, NumberWriter<T> writer> +size_t write_num_digits(substr buf, T v, size_t num_digits) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + size_t ret = writer(buf, v); + if(ret >= num_digits) + return ret; + else if(ret >= buf.len || num_digits > buf.len) + return num_digits; + C4_ASSERT(num_digits >= ret); + size_t delta = static_cast<size_t>(num_digits - ret); + memmove(buf.str + delta, buf.str, ret); + memset(buf.str, '0', delta); + return num_digits; +} +} // namespace detail + + +/** same as c4::write_dec(), but pad with zeroes on the left + * such that the resulting string is @p num_digits wide. + * If the given number is requires more than num_digits, then the number prevails. */ +template<class T> +C4_ALWAYS_INLINE size_t write_dec(substr buf, T val, size_t num_digits) noexcept +{ + return detail::write_num_digits<T, &write_dec<T>>(buf, val, num_digits); +} + +/** same as c4::write_hex(), but pad with zeroes on the left + * such that the resulting string is @p num_digits wide. + * If the given number is requires more than num_digits, then the number prevails. */ +template<class T> +C4_ALWAYS_INLINE size_t write_hex(substr buf, T val, size_t num_digits) noexcept +{ + return detail::write_num_digits<T, &write_hex<T>>(buf, val, num_digits); +} + +/** same as c4::write_bin(), but pad with zeroes on the left + * such that the resulting string is @p num_digits wide. + * If the given number is requires more than num_digits, then the number prevails. */ +template<class T> +C4_ALWAYS_INLINE size_t write_bin(substr buf, T val, size_t num_digits) noexcept +{ + return detail::write_num_digits<T, &write_bin<T>>(buf, val, num_digits); +} + +/** same as c4::write_oct(), but pad with zeroes on the left + * such that the resulting string is @p num_digits wide. + * If the given number is requires more than num_digits, then the number prevails. */ +template<class T> +C4_ALWAYS_INLINE size_t write_oct(substr buf, T val, size_t num_digits) noexcept +{ + return detail::write_num_digits<T, &write_oct<T>>(buf, val, num_digits); +} + +C4_SUPPRESS_WARNING_GCC_POP + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** read a decimal integer from a string. This is the + * lowest level (and the fastest) function to do this task. + * @note does not accept negative numbers + * @note The string must be trimmed. Whitespace is not accepted. + * @note the string must not be empty + * @note there is no check for overflow; the value wraps around + * in a way similar to the standard C/C++ overflow behavior. + * For example, `read_dec<int8_t>("128", &val)` returns true + * and val will be set to 0 because 127 is the max i8 value. + * @see overflows<T>() to find out if a number string overflows a type range + * @return true if the conversion was successful (no overflow check) */ +template<class I> +C4_ALWAYS_INLINE bool read_dec(csubstr s, I *C4_RESTRICT v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<I>::value); + C4_ASSERT(!s.empty()); + *v = 0; + for(char c : s) + { + if(C4_UNLIKELY(c < '0' || c > '9')) + return false; + *v = (*v) * I(10) + (I(c) - I('0')); + } + return true; +} + +/** read an hexadecimal integer from a string. This is the + * lowest level (and the fastest) function to do this task. + * @note does not accept negative numbers + * @note does not accept leading 0x or 0X + * @note the string must not be empty + * @note the string must be trimmed. Whitespace is not accepted. + * @note there is no check for overflow; the value wraps around + * in a way similar to the standard C/C++ overflow behavior. + * For example, `read_hex<int8_t>("80", &val)` returns true + * and val will be set to 0 because 7f is the max i8 value. + * @see overflows<T>() to find out if a number string overflows a type range + * @return true if the conversion was successful (no overflow check) */ +template<class I> +C4_ALWAYS_INLINE bool read_hex(csubstr s, I *C4_RESTRICT v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<I>::value); + C4_ASSERT(!s.empty()); + *v = 0; + for(char c : s) + { + I cv; + if(c >= '0' && c <= '9') + cv = I(c) - I('0'); + else if(c >= 'a' && c <= 'f') + cv = I(10) + (I(c) - I('a')); + else if(c >= 'A' && c <= 'F') + cv = I(10) + (I(c) - I('A')); + else + return false; + *v = (*v) * I(16) + cv; + } + return true; +} + +/** read a binary integer from a string. This is the + * lowest level (and the fastest) function to do this task. + * @note does not accept negative numbers + * @note does not accept leading 0b or 0B + * @note the string must not be empty + * @note the string must be trimmed. Whitespace is not accepted. + * @note there is no check for overflow; the value wraps around + * in a way similar to the standard C/C++ overflow behavior. + * For example, `read_bin<int8_t>("10000000", &val)` returns true + * and val will be set to 0 because 1111111 is the max i8 value. + * @see overflows<T>() to find out if a number string overflows a type range + * @return true if the conversion was successful (no overflow check) */ +template<class I> +C4_ALWAYS_INLINE bool read_bin(csubstr s, I *C4_RESTRICT v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<I>::value); + C4_ASSERT(!s.empty()); + *v = 0; + for(char c : s) + { + *v <<= 1; + if(c == '1') + *v |= 1; + else if(c != '0') + return false; + } + return true; +} + +/** read an octal integer from a string. This is the + * lowest level (and the fastest) function to do this task. + * @note does not accept negative numbers + * @note does not accept leading 0o or 0O + * @note the string must not be empty + * @note the string must be trimmed. Whitespace is not accepted. + * @note there is no check for overflow; the value wraps around + * in a way similar to the standard C/C++ overflow behavior. + * For example, `read_oct<int8_t>("200", &val)` returns true + * and val will be set to 0 because 177 is the max i8 value. + * @see overflows<T>() to find out if a number string overflows a type range + * @return true if the conversion was successful (no overflow check) */ +template<class I> +C4_ALWAYS_INLINE bool read_oct(csubstr s, I *C4_RESTRICT v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<I>::value); + C4_ASSERT(!s.empty()); + *v = 0; + for(char c : s) + { + if(C4_UNLIKELY(c < '0' || c > '7')) + return false; + *v = (*v) * I(8) + (I(c) - I('0')); + } + return true; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace detail { +inline size_t _itoa2buf(substr buf, size_t pos, csubstr val) noexcept +{ + C4_ASSERT(pos + val.len <= buf.len); + memcpy(buf.str + pos, val.str, val.len); + return pos + val.len; +} +inline size_t _itoa2bufwithdigits(substr buf, size_t pos, size_t num_digits, csubstr val) noexcept +{ + num_digits = num_digits > val.len ? num_digits - val.len : 0; + C4_ASSERT(num_digits + val.len <= buf.len); + for(size_t i = 0; i < num_digits; ++i) + _c4append('0'); + return detail::_itoa2buf(buf, pos, val); +} +template<class I> +C4_NO_INLINE size_t _itoadec2buf(substr buf) noexcept +{ + using digits_type = detail::charconv_digits<I>; + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec)) + return digits_type::maxdigits_dec; + buf.str[0] = '-'; + return detail::_itoa2buf(buf, 1, digits_type::min_value_dec()); +} +template<class I> +C4_NO_INLINE size_t _itoa2buf(substr buf, I radix) noexcept +{ + using digits_type = detail::charconv_digits<I>; + size_t pos = 0; + if(C4_LIKELY(buf.len > 0)) + buf.str[pos++] = '-'; + switch(radix) + { + case I(10): + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec)) + return digits_type::maxdigits_dec; + pos =_itoa2buf(buf, pos, digits_type::min_value_dec()); + break; + case I(16): + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_hex)) + return digits_type::maxdigits_hex; + buf.str[pos++] = '0'; + buf.str[pos++] = 'x'; + pos = _itoa2buf(buf, pos, digits_type::min_value_hex()); + break; + case I( 2): + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_bin)) + return digits_type::maxdigits_bin; + buf.str[pos++] = '0'; + buf.str[pos++] = 'b'; + pos = _itoa2buf(buf, pos, digits_type::min_value_bin()); + break; + case I( 8): + if(C4_UNLIKELY(buf.len < digits_type::maxdigits_oct)) + return digits_type::maxdigits_oct; + buf.str[pos++] = '0'; + buf.str[pos++] = 'o'; + pos = _itoa2buf(buf, pos, digits_type::min_value_oct()); + break; + } + return pos; +} +template<class I> +C4_NO_INLINE size_t _itoa2buf(substr buf, I radix, size_t num_digits) noexcept +{ + using digits_type = detail::charconv_digits<I>; + size_t pos = 0; + size_t needed_digits = 0; + if(C4_LIKELY(buf.len > 0)) + buf.str[pos++] = '-'; + switch(radix) + { + case I(10): + // add 1 to account for - + needed_digits = num_digits+1 > digits_type::maxdigits_dec ? num_digits+1 : digits_type::maxdigits_dec; + if(C4_UNLIKELY(buf.len < needed_digits)) + return needed_digits; + pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_dec()); + break; + case I(16): + // add 3 to account for -0x + needed_digits = num_digits+3 > digits_type::maxdigits_hex ? num_digits+3 : digits_type::maxdigits_hex; + if(C4_UNLIKELY(buf.len < needed_digits)) + return needed_digits; + buf.str[pos++] = '0'; + buf.str[pos++] = 'x'; + pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_hex()); + break; + case I( 2): + // add 3 to account for -0b + needed_digits = num_digits+3 > digits_type::maxdigits_bin ? num_digits+3 : digits_type::maxdigits_bin; + if(C4_UNLIKELY(buf.len < needed_digits)) + return needed_digits; + C4_ASSERT(buf.len >= digits_type::maxdigits_bin); + buf.str[pos++] = '0'; + buf.str[pos++] = 'b'; + pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_bin()); + break; + case I( 8): + // add 3 to account for -0o + needed_digits = num_digits+3 > digits_type::maxdigits_oct ? num_digits+3 : digits_type::maxdigits_oct; + if(C4_UNLIKELY(buf.len < needed_digits)) + return needed_digits; + C4_ASSERT(buf.len >= digits_type::maxdigits_oct); + buf.str[pos++] = '0'; + buf.str[pos++] = 'o'; + pos = _itoa2bufwithdigits(buf, pos, num_digits, digits_type::min_value_oct()); + break; + } + return pos; +} +} // namespace detail + + +/** convert an integral signed decimal to a string. + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t itoa(substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_signed<T>::value); + if(v >= T(0)) + { + // write_dec() checks the buffer size, so no need to check here + return write_dec(buf, v); + } + // when T is the min value (eg i8: -128), negating it + // will overflow, so treat the min as a special case + else if(C4_LIKELY(v != std::numeric_limits<T>::min())) + { + v = -v; + unsigned digits = digits_dec(v); + if(C4_LIKELY(buf.len >= digits + 1u)) + { + buf.str[0] = '-'; + write_dec_unchecked(buf.sub(1), v, digits); + } + return digits + 1u; + } + return detail::_itoadec2buf<T>(buf); +} + +/** convert an integral signed integer to a string, using a specific + * radix. The radix must be 2, 8, 10 or 16. + * + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix) noexcept +{ + C4_STATIC_ASSERT(std::is_signed<T>::value); + C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16); + C4_SUPPRESS_WARNING_GCC_PUSH + #if (defined(__GNUC__) && (__GNUC__ >= 7)) + C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here + #endif + // when T is the min value (eg i8: -128), negating it + // will overflow, so treat the min as a special case + if(C4_LIKELY(v != std::numeric_limits<T>::min())) + { + unsigned pos = 0; + if(v < 0) + { + v = -v; + if(C4_LIKELY(buf.len > 0)) + buf.str[pos] = '-'; + ++pos; + } + unsigned digits = 0; + switch(radix) + { + case T(10): + digits = digits_dec(v); + if(C4_LIKELY(buf.len >= pos + digits)) + write_dec_unchecked(buf.sub(pos), v, digits); + break; + case T(16): + digits = digits_hex(v); + if(C4_LIKELY(buf.len >= pos + 2u + digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'x'; + write_hex_unchecked(buf.sub(pos + 2), v, digits); + } + digits += 2u; + break; + case T(2): + digits = digits_bin(v); + if(C4_LIKELY(buf.len >= pos + 2u + digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'b'; + write_bin_unchecked(buf.sub(pos + 2), v, digits); + } + digits += 2u; + break; + case T(8): + digits = digits_oct(v); + if(C4_LIKELY(buf.len >= pos + 2u + digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'o'; + write_oct_unchecked(buf.sub(pos + 2), v, digits); + } + digits += 2u; + break; + } + return pos + digits; + } + C4_SUPPRESS_WARNING_GCC_POP + // when T is the min value (eg i8: -128), negating it + // will overflow + return detail::_itoa2buf<T>(buf, radix); +} + + +/** same as c4::itoa(), but pad with zeroes on the left such that the + * resulting string is @p num_digits wide, not accounting for radix + * prefix (0x,0o,0b). The @p radix must be 2, 8, 10 or 16. + * + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix, size_t num_digits) noexcept +{ + C4_STATIC_ASSERT(std::is_signed<T>::value); + C4_ASSERT(radix == 2 || radix == 8 || radix == 10 || radix == 16); + C4_SUPPRESS_WARNING_GCC_PUSH + #if (defined(__GNUC__) && (__GNUC__ >= 7)) + C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc has a false positive here + #endif + // when T is the min value (eg i8: -128), negating it + // will overflow, so treat the min as a special case + if(C4_LIKELY(v != std::numeric_limits<T>::min())) + { + unsigned pos = 0; + if(v < 0) + { + v = -v; + if(C4_LIKELY(buf.len > 0)) + buf.str[pos] = '-'; + ++pos; + } + unsigned total_digits = 0; + switch(radix) + { + case T(10): + total_digits = digits_dec(v); + total_digits = pos + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + write_dec(buf.sub(pos), v, num_digits); + break; + case T(16): + total_digits = digits_hex(v); + total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'x'; + write_hex(buf.sub(pos + 2), v, num_digits); + } + break; + case T(2): + total_digits = digits_bin(v); + total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'b'; + write_bin(buf.sub(pos + 2), v, num_digits); + } + break; + case T(8): + total_digits = digits_oct(v); + total_digits = pos + 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[pos + 0] = '0'; + buf.str[pos + 1] = 'o'; + write_oct(buf.sub(pos + 2), v, num_digits); + } + break; + } + return total_digits; + } + C4_SUPPRESS_WARNING_GCC_POP + // when T is the min value (eg i8: -128), negating it + // will overflow + return detail::_itoa2buf<T>(buf, radix, num_digits); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** convert an integral unsigned decimal to a string. + * + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t utoa(substr buf, T v) noexcept +{ + C4_STATIC_ASSERT(std::is_unsigned<T>::value); + // write_dec() does the buffer length check, so no need to check here + return write_dec(buf, v); +} + +/** convert an integral unsigned integer to a string, using a specific + * radix. The radix must be 2, 8, 10 or 16. + * + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix) noexcept +{ + C4_STATIC_ASSERT(std::is_unsigned<T>::value); + C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8); + unsigned digits = 0; + switch(radix) + { + case T(10): + digits = digits_dec(v); + if(C4_LIKELY(buf.len >= digits)) + write_dec_unchecked(buf, v, digits); + break; + case T(16): + digits = digits_hex(v); + if(C4_LIKELY(buf.len >= digits+2u)) + { + buf.str[0] = '0'; + buf.str[1] = 'x'; + write_hex_unchecked(buf.sub(2), v, digits); + } + digits += 2u; + break; + case T(2): + digits = digits_bin(v); + if(C4_LIKELY(buf.len >= digits+2u)) + { + buf.str[0] = '0'; + buf.str[1] = 'b'; + write_bin_unchecked(buf.sub(2), v, digits); + } + digits += 2u; + break; + case T(8): + digits = digits_oct(v); + if(C4_LIKELY(buf.len >= digits+2u)) + { + buf.str[0] = '0'; + buf.str[1] = 'o'; + write_oct_unchecked(buf.sub(2), v, digits); + } + digits += 2u; + break; + } + return digits; +} + +/** same as c4::utoa(), but pad with zeroes on the left such that the + * resulting string is @p num_digits wide. The @p radix must be 2, + * 8, 10 or 16. + * + * @note the resulting string is NOT zero-terminated. + * @note it is ok to call this with an empty or too-small buffer; + * no writes will occur, and the needed size will be returned + * @return the number of characters required for the buffer. */ +template<class T> +C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix, size_t num_digits) noexcept +{ + C4_STATIC_ASSERT(std::is_unsigned<T>::value); + C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8); + unsigned total_digits = 0; + switch(radix) + { + case T(10): + total_digits = digits_dec(v); + total_digits = (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + write_dec(buf, v, num_digits); + break; + case T(16): + total_digits = digits_hex(v); + total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[0] = '0'; + buf.str[1] = 'x'; + write_hex(buf.sub(2), v, num_digits); + } + break; + case T(2): + total_digits = digits_bin(v); + total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[0] = '0'; + buf.str[1] = 'b'; + write_bin(buf.sub(2), v, num_digits); + } + break; + case T(8): + total_digits = digits_oct(v); + total_digits = 2u + (unsigned)(num_digits > total_digits ? num_digits : total_digits); + if(C4_LIKELY(buf.len >= total_digits)) + { + buf.str[0] = '0'; + buf.str[1] = 'o'; + write_oct(buf.sub(2), v, num_digits); + } + break; + } + return total_digits; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** Convert a trimmed string to a signed integral value. The input + * string can be formatted as decimal, binary (prefix 0b or 0B), octal + * (prefix 0o or 0O) or hexadecimal (prefix 0x or 0X). Strings with + * leading zeroes are considered as decimal and not octal (unlike the + * C/C++ convention). Every character in the input string is read for + * the conversion; the input string must not contain any leading or + * trailing whitespace. + * + * @return true if the conversion was successful. + * + * @note overflow is not detected: the return status is true even if + * the conversion would return a value outside of the type's range, in + * which case the result will wrap around the type's range. + * This is similar to native behavior. + * + * @note a positive sign is not accepted. ie, the string must not + * start with '+' + * + * @see atoi_first() if the string is not trimmed to the value to read. */ +template<class T> +C4_ALWAYS_INLINE bool atoi(csubstr str, T * C4_RESTRICT v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_STATIC_ASSERT(std::is_signed<T>::value); + + if(C4_UNLIKELY(str.len == 0)) + return false; + + C4_ASSERT(str.str[0] != '+'); + + T sign = 1; + size_t start = 0; + if(str.str[0] == '-') + { + if(C4_UNLIKELY(str.len == ++start)) + return false; + sign = -1; + } + + bool parsed_ok = true; + if(str.str[start] != '0') // this should be the common case, so put it first + { + parsed_ok = read_dec(str.sub(start), v); + } + else if(str.len > start + 1) + { + // starts with 0: is it 0x, 0o, 0b? + const char pfx = str.str[start + 1]; + if(pfx == 'x' || pfx == 'X') + parsed_ok = str.len > start + 2 && read_hex(str.sub(start + 2), v); + else if(pfx == 'b' || pfx == 'B') + parsed_ok = str.len > start + 2 && read_bin(str.sub(start + 2), v); + else if(pfx == 'o' || pfx == 'O') + parsed_ok = str.len > start + 2 && read_oct(str.sub(start + 2), v); + else + parsed_ok = read_dec(str.sub(start + 1), v); + } + else + { + parsed_ok = read_dec(str.sub(start), v); + } + if(C4_LIKELY(parsed_ok)) + *v *= sign; + return parsed_ok; +} + + +/** Select the next range of characters in the string that can be parsed + * as a signed integral value, and convert it using atoi(). Leading + * whitespace (space, newline, tabs) is skipped. + * @return the number of characters read for conversion, or csubstr::npos if the conversion failed + * @see atoi() if the string is already trimmed to the value to read. + * @see csubstr::first_int_span() */ +template<class T> +C4_ALWAYS_INLINE size_t atoi_first(csubstr str, T * C4_RESTRICT v) +{ + csubstr trimmed = str.first_int_span(); + if(trimmed.len == 0) + return csubstr::npos; + if(atoi(trimmed, v)) + return static_cast<size_t>(trimmed.end() - str.begin()); + return csubstr::npos; +} + + +//----------------------------------------------------------------------------- + +/** Convert a trimmed string to an unsigned integral value. The string can be + * formatted as decimal, binary (prefix 0b or 0B), octal (prefix 0o or 0O) + * or hexadecimal (prefix 0x or 0X). Every character in the input string is read + * for the conversion; it must not contain any leading or trailing whitespace. + * + * @return true if the conversion was successful. + * + * @note overflow is not detected: the return status is true even if + * the conversion would return a value outside of the type's range, in + * which case the result will wrap around the type's range. + * + * @note If the string has a minus character, the return status + * will be false. + * + * @see atou_first() if the string is not trimmed to the value to read. */ +template<class T> +bool atou(csubstr str, T * C4_RESTRICT v) noexcept +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + + if(C4_UNLIKELY(str.len == 0 || str.front() == '-')) + return false; + + bool parsed_ok = true; + if(str.str[0] != '0') + { + parsed_ok = read_dec(str, v); + } + else + { + if(str.len > 1) + { + const char pfx = str.str[1]; + if(pfx == 'x' || pfx == 'X') + parsed_ok = str.len > 2 && read_hex(str.sub(2), v); + else if(pfx == 'b' || pfx == 'B') + parsed_ok = str.len > 2 && read_bin(str.sub(2), v); + else if(pfx == 'o' || pfx == 'O') + parsed_ok = str.len > 2 && read_oct(str.sub(2), v); + else + parsed_ok = read_dec(str, v); + } + else + { + *v = 0; // we know the first character is 0 + } + } + return parsed_ok; +} + + +/** Select the next range of characters in the string that can be parsed + * as an unsigned integral value, and convert it using atou(). Leading + * whitespace (space, newline, tabs) is skipped. + * @return the number of characters read for conversion, or csubstr::npos if the conversion faileds + * @see atou() if the string is already trimmed to the value to read. + * @see csubstr::first_uint_span() */ +template<class T> +C4_ALWAYS_INLINE size_t atou_first(csubstr str, T *v) +{ + csubstr trimmed = str.first_uint_span(); + if(trimmed.len == 0) + return csubstr::npos; + if(atou(trimmed, v)) + return static_cast<size_t>(trimmed.end() - str.begin()); + return csubstr::npos; +} + + +#ifdef _MSC_VER +# pragma warning(pop) +#elif defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +namespace detail { +inline bool check_overflow(csubstr str, csubstr limit) noexcept +{ + if(str.len == limit.len) + { + for(size_t i = 0; i < limit.len; ++i) + { + if(str[i] < limit[i]) + return false; + else if(str[i] > limit[i]) + return true; + } + return false; + } + else + return str.len > limit.len; +} +} // namespace detail + + +/** Test if the following string would overflow when converted to associated + * types. + * @return true if number will overflow, false if it fits (or doesn't parse) + */ +template<class T> +auto overflows(csubstr str) noexcept + -> typename std::enable_if<std::is_unsigned<T>::value, bool>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + + if(C4_UNLIKELY(str.len == 0)) + { + return false; + } + else if(str.str[0] == '0') + { + if (str.len == 1) + return false; + switch (str.str[1]) + { + case 'x': + case 'X': + { + size_t fno = str.first_not_of('0', 2); + if (fno == csubstr::npos) + return false; + return !(str.len <= fno + (sizeof(T) * 2)); + } + case 'b': + case 'B': + { + size_t fno = str.first_not_of('0', 2); + if (fno == csubstr::npos) + return false; + return !(str.len <= fno +(sizeof(T) * 8)); + } + case 'o': + case 'O': + { + size_t fno = str.first_not_of('0', 2); + if(fno == csubstr::npos) + return false; + return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno)); + } + default: + { + size_t fno = str.first_not_of('0', 1); + if(fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec()); + } + } + } + else if(C4_UNLIKELY(str[0] == '-')) + { + return true; + } + else + { + return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec()); + } +} + + +/** Test if the following string would overflow when converted to associated + * types. + * @return true if number will overflow, false if it fits (or doesn't parse) + */ +template<class T> +auto overflows(csubstr str) + -> typename std::enable_if<std::is_signed<T>::value, bool>::type +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + if(C4_UNLIKELY(str.len == 0)) + return false; + if(str.str[0] == '-') + { + if(str.str[1] == '0') + { + if(str.len == 2) + return false; + switch(str.str[2]) + { + case 'x': + case 'X': + { + size_t fno = str.first_not_of('0', 3); + if (fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_hex()); + } + case 'b': + case 'B': + { + size_t fno = str.first_not_of('0', 3); + if (fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_bin()); + } + case 'o': + case 'O': + { + size_t fno = str.first_not_of('0', 3); + if(fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_oct()); + } + default: + { + size_t fno = str.first_not_of('0', 2); + if(fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::min_value_dec()); + } + } + } + else + return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec()); + } + else if(str.str[0] == '0') + { + if (str.len == 1) + return false; + switch(str.str[1]) + { + case 'x': + case 'X': + { + size_t fno = str.first_not_of('0', 2); + if (fno == csubstr::npos) + return false; + const size_t len = str.len - fno; + return !((len < sizeof (T) * 2) || (len == sizeof(T) * 2 && str[fno] <= '7')); + } + case 'b': + case 'B': + { + size_t fno = str.first_not_of('0', 2); + if (fno == csubstr::npos) + return false; + return !(str.len <= fno + (sizeof(T) * 8 - 1)); + } + case 'o': + case 'O': + { + size_t fno = str.first_not_of('0', 2); + if(fno == csubstr::npos) + return false; + return detail::charconv_digits<T>::is_oct_overflow(str.sub(fno)); + } + default: + { + size_t fno = str.first_not_of('0', 1); + if(fno == csubstr::npos) + return false; + return detail::check_overflow(str.sub(fno), detail::charconv_digits<T>::max_value_dec()); + } + } + } + else + return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec()); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace detail { + + +#if (!C4CORE_HAVE_STD_FROMCHARS) +/** @see http://www.exploringbinary.com/ for many good examples on float-str conversion */ +template<size_t N> +void get_real_format_str(char (& C4_RESTRICT fmt)[N], int precision, RealFormat_e formatting, const char* length_modifier="") +{ + int iret; + if(precision == -1) + iret = snprintf(fmt, sizeof(fmt), "%%%s%c", length_modifier, formatting); + else if(precision == 0) + iret = snprintf(fmt, sizeof(fmt), "%%.%s%c", length_modifier, formatting); + else + iret = snprintf(fmt, sizeof(fmt), "%%.%d%s%c", precision, length_modifier, formatting); + C4_ASSERT(iret >= 2 && size_t(iret) < sizeof(fmt)); + C4_UNUSED(iret); +} + + +/** @todo we're depending on snprintf()/sscanf() for converting to/from + * floating point numbers. Apparently, this increases the binary size + * by a considerable amount. There are some lightweight printf + * implementations: + * + * @see http://www.sparetimelabs.com/tinyprintf/tinyprintf.php (BSD) + * @see https://github.com/weiss/c99-snprintf + * @see https://github.com/nothings/stb/blob/master/stb_sprintf.h + * @see http://www.exploringbinary.com/ + * @see https://blog.benoitblanchon.fr/lightweight-float-to-string/ + * @see http://www.ryanjuckett.com/programming/printing-floating-point-numbers/ + */ +template<class T> +size_t print_one(substr str, const char* full_fmt, T v) +{ +#ifdef _MSC_VER + /** use _snprintf() to prevent early termination of the output + * for writing the null character at the last position + * @see https://msdn.microsoft.com/en-us/library/2ts7cx93.aspx */ + int iret = _snprintf(str.str, str.len, full_fmt, v); + if(iret < 0) + { + /* when buf.len is not enough, VS returns a negative value. + * so call it again with a negative value for getting an + * actual length of the string */ + iret = snprintf(nullptr, 0, full_fmt, v); + C4_ASSERT(iret > 0); + } + size_t ret = (size_t) iret; + return ret; +#else + int iret = snprintf(str.str, str.len, full_fmt, v); + C4_ASSERT(iret >= 0); + size_t ret = (size_t) iret; + if(ret >= str.len) + ++ret; /* snprintf() reserves the last character to write \0 */ + return ret; +#endif +} +#endif // (!C4CORE_HAVE_STD_FROMCHARS) + + +#if (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT) +/** scans a string using the given type format, while at the same time + * allowing non-null-terminated strings AND guaranteeing that the given + * string length is strictly respected, so that no buffer overflows + * might occur. */ +template<typename T> +inline size_t scan_one(csubstr str, const char *type_fmt, T *v) +{ + /* snscanf() is absolutely needed here as we must be sure that + * str.len is strictly respected, because substr is + * generally not null-terminated. + * + * Alas, there is no snscanf(). + * + * So we fake it by using a dynamic format with an explicit + * field size set to the length of the given span. + * This trick is taken from: + * https://stackoverflow.com/a/18368910/5875572 */ + + /* this is the actual format we'll use for scanning */ + char fmt[16]; + + /* write the length into it. Eg "%12f". + * Also, get the number of characters read from the string. + * So the final format ends up as "%12f%n"*/ + int iret = std::snprintf(fmt, sizeof(fmt), "%%" "%zu" "%s" "%%n", str.len, type_fmt); + /* no nasty surprises, please! */ + C4_ASSERT(iret >= 0 && size_t(iret) < C4_COUNTOF(fmt)); + + /* now we scan with confidence that the span length is respected */ + int num_chars; + iret = std::sscanf(str.str, fmt, v, &num_chars); + /* scanf returns the number of successful conversions */ + if(iret != 1) return csubstr::npos; + C4_ASSERT(num_chars >= 0); + return (size_t)(num_chars); +} +#endif // (!C4CORE_HAVE_STD_FROMCHARS) && (!C4CORE_HAVE_FAST_FLOAT) + + +#if C4CORE_HAVE_STD_TOCHARS +template<class T> +C4_ALWAYS_INLINE size_t rtoa(substr buf, T v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept +{ + std::to_chars_result result; + size_t pos = 0; + if(formatting == FTOA_HEXA) + { + if(buf.len > size_t(2)) + { + buf.str[0] = '0'; + buf.str[1] = 'x'; + } + pos += size_t(2); + } + if(precision == -1) + result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting); + else + result = std::to_chars(buf.str + pos, buf.str + buf.len, v, (std::chars_format)formatting, precision); + if(result.ec == std::errc()) + { + // all good, no errors. + C4_ASSERT(result.ptr >= buf.str); + ptrdiff_t delta = result.ptr - buf.str; + return static_cast<size_t>(delta); + } + C4_ASSERT(result.ec == std::errc::value_too_large); + // This is unfortunate. + // + // When the result can't fit in the given buffer, + // std::to_chars() returns the end pointer it was originally + // given, which is useless because here we would like to know + // _exactly_ how many characters the buffer must have to fit + // the result. + // + // So we take the pessimistic view, and assume as many digits + // as could ever be required: + size_t ret = static_cast<size_t>(std::numeric_limits<T>::max_digits10); + return ret > buf.len ? ret : buf.len + 1; +} +#endif // C4CORE_HAVE_STD_TOCHARS + + +#if C4CORE_HAVE_FAST_FLOAT +template<class T> +C4_ALWAYS_INLINE bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept +{ + C4_ASSERT(s.len > 0); + C4_ASSERT(s.str[0] != '-'); + C4_ASSERT(s.str[0] != '+'); + C4_ASSERT(!s.begins_with("0x")); + C4_ASSERT(!s.begins_with("0X")); + size_t pos = 0; + // integer part + for( ; pos < s.len; ++pos) + { + const char c = s.str[pos]; + if(c >= '0' && c <= '9') + *val = *val * T(16) + T(c - '0'); + else if(c >= 'a' && c <= 'f') + *val = *val * T(16) + T(c - 'a'); + else if(c >= 'A' && c <= 'F') + *val = *val * T(16) + T(c - 'A'); + else if(c == '.') + { + ++pos; + break; // follow on to mantissa + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power; // no mantissa given, jump to power + } + else + { + return false; + } + } + // mantissa + { + // 0.0625 == 1/16 == value of first digit after the comma + for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16)) + { + const char c = s.str[pos]; + if(c >= '0' && c <= '9') + *val += digit * T(c - '0'); + else if(c >= 'a' && c <= 'f') + *val += digit * T(c - 'a'); + else if(c >= 'A' && c <= 'F') + *val += digit * T(c - 'A'); + else if(c == 'p' || c == 'P') + { + ++pos; + goto power; // mantissa finished, jump to power + } + else + { + return false; + } + } + } + return true; +power: + if(C4_LIKELY(pos < s.len)) + { + if(s.str[pos] == '+') // atoi() cannot handle a leading '+' + ++pos; + if(C4_LIKELY(pos < s.len)) + { + int16_t powval = {}; + if(C4_LIKELY(atoi(s.sub(pos), &powval))) + { + *val *= ipow<T, int16_t, 16>(powval); + return true; + } + } + } + return false; +} +#endif + +} // namespace detail + + +#undef _c4appendhex +#undef _c4append + + +/** Convert a single-precision real number to string. The string will + * in general be NOT null-terminated. For FTOA_FLEX, \p precision is + * the number of significand digits. Otherwise \p precision is the + * number of decimals. It is safe to call this function with an empty + * or too-small buffer. + * + * @return the size of the buffer needed to write the number + */ +C4_ALWAYS_INLINE size_t ftoa(substr str, float v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept +{ +#if C4CORE_HAVE_STD_TOCHARS + return detail::rtoa(str, v, precision, formatting); +#else + char fmt[16]; + detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/""); + return detail::print_one(str, fmt, v); +#endif +} + + +/** Convert a double-precision real number to string. The string will + * in general be NOT null-terminated. For FTOA_FLEX, \p precision is + * the number of significand digits. Otherwise \p precision is the + * number of decimals. It is safe to call this function with an empty + * or too-small buffer. + * + * @return the size of the buffer needed to write the number + */ +C4_ALWAYS_INLINE size_t dtoa(substr str, double v, int precision=-1, RealFormat_e formatting=FTOA_FLEX) noexcept +{ +#if C4CORE_HAVE_STD_TOCHARS + return detail::rtoa(str, v, precision, formatting); +#else + char fmt[16]; + detail::get_real_format_str(fmt, precision, formatting, /*length_modifier*/"l"); + return detail::print_one(str, fmt, v); +#endif +} + + +/** Convert a string to a single precision real number. + * The input string must be trimmed to the value, ie + * no leading or trailing whitespace can be present. + * @return true iff the conversion succeeded + * @see atof_first() if the string is not trimmed + */ +C4_ALWAYS_INLINE bool atof(csubstr str, float * C4_RESTRICT v) noexcept +{ + C4_ASSERT(str.len > 0); + C4_ASSERT(str.triml(" \r\t\n").len == str.len); +#if C4CORE_HAVE_FAST_FLOAT + // fastfloat cannot parse hexadecimal floats + bool isneg = (str.str[0] == '-'); + csubstr rem = str.sub(isneg || str.str[0] == '+'); + if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) + { + fast_float::from_chars_result result; + result = fast_float::from_chars(str.str, str.str + str.len, *v); + return result.ec == std::errc(); + } + else if(detail::scan_rhex(rem.sub(2), v)) + { + *v *= isneg ? -1.f : 1.f; + return true; + } + return false; +#elif C4CORE_HAVE_STD_FROMCHARS + std::from_chars_result result; + result = std::from_chars(str.str, str.str + str.len, *v); + return result.ec == std::errc(); +#else + csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+'); + if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) + return detail::scan_one(str, "f", v) != csubstr::npos; + else + return detail::scan_one(str, "a", v) != csubstr::npos; +#endif +} + + +/** Convert a string to a double precision real number. + * The input string must be trimmed to the value, ie + * no leading or trailing whitespace can be present. + * @return true iff the conversion succeeded + * @see atod_first() if the string is not trimmed + */ +C4_ALWAYS_INLINE bool atod(csubstr str, double * C4_RESTRICT v) noexcept +{ + C4_ASSERT(str.triml(" \r\t\n").len == str.len); +#if C4CORE_HAVE_FAST_FLOAT + // fastfloat cannot parse hexadecimal floats + bool isneg = (str.str[0] == '-'); + csubstr rem = str.sub(isneg || str.str[0] == '+'); + if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) + { + fast_float::from_chars_result result; + result = fast_float::from_chars(str.str, str.str + str.len, *v); + return result.ec == std::errc(); + } + else if(detail::scan_rhex(rem.sub(2), v)) + { + *v *= isneg ? -1. : 1.; + return true; + } + return false; +#elif C4CORE_HAVE_STD_FROMCHARS + std::from_chars_result result; + result = std::from_chars(str.str, str.str + str.len, *v); + return result.ec == std::errc(); +#else + csubstr rem = str.sub(str.str[0] == '-' || str.str[0] == '+'); + if(!(rem.len >= 2 && (rem.str[0] == '0' && (rem.str[1] == 'x' || rem.str[1] == 'X')))) + return detail::scan_one(str, "lf", v) != csubstr::npos; + else + return detail::scan_one(str, "la", v) != csubstr::npos; +#endif +} + + +/** Convert a string to a single precision real number. + * Leading whitespace is skipped until valid characters are found. + * @return the number of characters read from the string, or npos if + * conversion was not successful or if the string was empty */ +inline size_t atof_first(csubstr str, float * C4_RESTRICT v) noexcept +{ + csubstr trimmed = str.first_real_span(); + if(trimmed.len == 0) + return csubstr::npos; + if(atof(trimmed, v)) + return static_cast<size_t>(trimmed.end() - str.begin()); + return csubstr::npos; +} + + +/** Convert a string to a double precision real number. + * Leading whitespace is skipped until valid characters are found. + * @return the number of characters read from the string, or npos if + * conversion was not successful or if the string was empty */ +inline size_t atod_first(csubstr str, double * C4_RESTRICT v) noexcept +{ + csubstr trimmed = str.first_real_span(); + if(trimmed.len == 0) + return csubstr::npos; + if(atod(trimmed, v)) + return static_cast<size_t>(trimmed.end() - str.begin()); + return csubstr::npos; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// generic versions + +C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v) noexcept { return write_dec(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v) noexcept { return write_dec(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v) noexcept { return write_dec(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v) noexcept { return write_dec(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v) noexcept { return itoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v) noexcept { return itoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v) noexcept { return itoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v) noexcept { return itoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, float v) noexcept { return ftoa(s, v); } +C4_ALWAYS_INLINE size_t xtoa(substr s, double v) noexcept { return dtoa(s, v); } + +C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v, uint8_t radix) noexcept { return utoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v, uint16_t radix) noexcept { return utoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v, uint32_t radix) noexcept { return utoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v, uint64_t radix) noexcept { return utoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v, int8_t radix) noexcept { return itoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v, int16_t radix) noexcept { return itoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v, int32_t radix) noexcept { return itoa(s, v, radix); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix) noexcept { return itoa(s, v, radix); } + +C4_ALWAYS_INLINE size_t xtoa(substr s, uint8_t v, uint8_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint16_t v, uint16_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint32_t v, uint32_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, uint64_t v, uint64_t radix, size_t num_digits) noexcept { return utoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int8_t v, int8_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int16_t v, int16_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int32_t v, int32_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); } +C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix, size_t num_digits) noexcept { return itoa(s, v, radix, num_digits); } + +C4_ALWAYS_INLINE size_t xtoa(substr s, float v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return ftoa(s, v, precision, formatting); } +C4_ALWAYS_INLINE size_t xtoa(substr s, double v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return dtoa(s, v, precision, formatting); } + +C4_ALWAYS_INLINE bool atox(csubstr s, uint8_t *C4_RESTRICT v) noexcept { return atou(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, uint16_t *C4_RESTRICT v) noexcept { return atou(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, uint32_t *C4_RESTRICT v) noexcept { return atou(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, uint64_t *C4_RESTRICT v) noexcept { return atou(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, int8_t *C4_RESTRICT v) noexcept { return atoi(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, int16_t *C4_RESTRICT v) noexcept { return atoi(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, int32_t *C4_RESTRICT v) noexcept { return atoi(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, int64_t *C4_RESTRICT v) noexcept { return atoi(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, float *C4_RESTRICT v) noexcept { return atof(s, v); } +C4_ALWAYS_INLINE bool atox(csubstr s, double *C4_RESTRICT v) noexcept { return atod(s, v); } + +C4_ALWAYS_INLINE size_t to_chars(substr buf, uint8_t v) noexcept { return write_dec(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, uint16_t v) noexcept { return write_dec(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, uint32_t v) noexcept { return write_dec(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, uint64_t v) noexcept { return write_dec(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, int8_t v) noexcept { return itoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, int16_t v) noexcept { return itoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, int32_t v) noexcept { return itoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, int64_t v) noexcept { return itoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, float v) noexcept { return ftoa(buf, v); } +C4_ALWAYS_INLINE size_t to_chars(substr buf, double v) noexcept { return dtoa(buf, v); } + +C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, float *C4_RESTRICT v) noexcept { return atof(buf, v); } +C4_ALWAYS_INLINE bool from_chars(csubstr buf, double *C4_RESTRICT v) noexcept { return atod(buf, v); } + +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint8_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint16_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint32_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, uint64_t *C4_RESTRICT v) noexcept { return atou_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int8_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int16_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int32_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int64_t *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, float *C4_RESTRICT v) noexcept { return atof_first(buf, v); } +C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) noexcept { return atod_first(buf, v); } + + +//----------------------------------------------------------------------------- +// on some platforms, (unsigned) int and (unsigned) long +// are not any of the fixed length types above + +#define _C4_IF_NOT_FIXED_LENGTH_I(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std:: is_signed<T>::value && !is_fixed_length<T>::value_i, ty> +#define _C4_IF_NOT_FIXED_LENGTH_U(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && !is_fixed_length<T>::value_u, ty> + +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type xtoa(substr buf, T v) noexcept { return itoa(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type xtoa(substr buf, T v) noexcept { return write_dec(buf, v); } + +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) noexcept { return atou(buf, v); } + +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type to_chars(substr buf, T v) noexcept { return itoa(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type to_chars(substr buf, T v) noexcept { return write_dec(buf, v); } + +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return atou(buf, v); } + +template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi_first(buf, v); } +template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return atou_first(buf, v); } + +#undef _C4_IF_NOT_FIXED_LENGTH_I +#undef _C4_IF_NOT_FIXED_LENGTH_U + + +//----------------------------------------------------------------------------- +// for pointers + +template <class T> C4_ALWAYS_INLINE size_t xtoa(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); } +template <class T> C4_ALWAYS_INLINE bool atox(csubstr s, T **v) noexcept { intptr_t tmp; bool ret = atox(s, &tmp); if(ret) { *v = (T*)tmp; } return ret; } +template <class T> C4_ALWAYS_INLINE size_t to_chars(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); } +template <class T> C4_ALWAYS_INLINE bool from_chars(csubstr buf, T **v) noexcept { intptr_t tmp; bool ret = from_chars(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; } +template <class T> C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, T **v) noexcept { intptr_t tmp; bool ret = from_chars_first(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; } + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** call to_chars() and return a substr consisting of the + * written portion of the input buffer. Ie, same as to_chars(), + * but return a substr instead of a size_t. + * + * @see to_chars() */ +template<class T> +C4_ALWAYS_INLINE substr to_chars_sub(substr buf, T const& C4_RESTRICT v) noexcept +{ + size_t sz = to_chars(buf, v); + return buf.left_of(sz <= buf.len ? sz : buf.len); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// bool implementation + +C4_ALWAYS_INLINE size_t to_chars(substr buf, bool v) noexcept +{ + int val = v; + return to_chars(buf, val); +} + +inline bool from_chars(csubstr buf, bool * C4_RESTRICT v) noexcept +{ + if(buf == '0') + { + *v = false; return true; + } + else if(buf == '1') + { + *v = true; return true; + } + else if(buf == "false") + { + *v = false; return true; + } + else if(buf == "true") + { + *v = true; return true; + } + else if(buf == "False") + { + *v = false; return true; + } + else if(buf == "True") + { + *v = true; return true; + } + else if(buf == "FALSE") + { + *v = false; return true; + } + else if(buf == "TRUE") + { + *v = true; return true; + } + // fallback to c-style int bools + int val = 0; + bool ret = from_chars(buf, &val); + if(C4_LIKELY(ret)) + { + *v = (val != 0); + } + return ret; +} + +inline size_t from_chars_first(csubstr buf, bool * C4_RESTRICT v) noexcept +{ + csubstr trimmed = buf.first_non_empty_span(); + if(trimmed.len == 0 || !from_chars(buf, v)) + return csubstr::npos; + return trimmed.len; +} + + +//----------------------------------------------------------------------------- +// single-char implementation + +inline size_t to_chars(substr buf, char v) noexcept +{ + if(buf.len > 0) + buf[0] = v; + return 1; +} + +/** extract a single character from a substring + * @note to extract a string instead and not just a single character, use the csubstr overload */ +inline bool from_chars(csubstr buf, char * C4_RESTRICT v) noexcept +{ + if(buf.len != 1) + return false; + *v = buf[0]; + return true; +} + +inline size_t from_chars_first(csubstr buf, char * C4_RESTRICT v) noexcept +{ + if(buf.len < 1) + return csubstr::npos; + *v = buf[0]; + return 1; +} + + +//----------------------------------------------------------------------------- +// csubstr implementation + +inline size_t to_chars(substr buf, csubstr v) noexcept +{ + C4_ASSERT(!buf.overlaps(v)); + size_t len = buf.len < v.len ? buf.len : v.len; + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len) + { + C4_ASSERT(buf.str != nullptr); + C4_ASSERT(v.str != nullptr); + memcpy(buf.str, v.str, len); + } + return v.len; +} + +inline bool from_chars(csubstr buf, csubstr *C4_RESTRICT v) noexcept +{ + *v = buf; + return true; +} + +inline size_t from_chars_first(substr buf, csubstr * C4_RESTRICT v) noexcept +{ + csubstr trimmed = buf.first_non_empty_span(); + if(trimmed.len == 0) + return csubstr::npos; + *v = trimmed; + return static_cast<size_t>(trimmed.end() - buf.begin()); +} + + +//----------------------------------------------------------------------------- +// substr + +inline size_t to_chars(substr buf, substr v) noexcept +{ + C4_ASSERT(!buf.overlaps(v)); + size_t len = buf.len < v.len ? buf.len : v.len; + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len) + { + C4_ASSERT(buf.str != nullptr); + C4_ASSERT(v.str != nullptr); + memcpy(buf.str, v.str, len); + } + return v.len; +} + +inline bool from_chars(csubstr buf, substr * C4_RESTRICT v) noexcept +{ + C4_ASSERT(!buf.overlaps(*v)); + // is the destination buffer wide enough? + if(v->len >= buf.len) + { + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(buf.len) + { + C4_ASSERT(buf.str != nullptr); + C4_ASSERT(v->str != nullptr); + memcpy(v->str, buf.str, buf.len); + } + v->len = buf.len; + return true; + } + return false; +} + +inline size_t from_chars_first(csubstr buf, substr * C4_RESTRICT v) noexcept +{ + csubstr trimmed = buf.first_non_empty_span(); + C4_ASSERT(!trimmed.overlaps(*v)); + if(C4_UNLIKELY(trimmed.len == 0)) + return csubstr::npos; + size_t len = trimmed.len > v->len ? v->len : trimmed.len; + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len) + { + C4_ASSERT(buf.str != nullptr); + C4_ASSERT(v->str != nullptr); + memcpy(v->str, trimmed.str, len); + } + if(C4_UNLIKELY(trimmed.len > v->len)) + return csubstr::npos; + return static_cast<size_t>(trimmed.end() - buf.begin()); +} + + +//----------------------------------------------------------------------------- + +template<size_t N> +inline size_t to_chars(substr buf, const char (& C4_RESTRICT v)[N]) noexcept +{ + csubstr sp(v); + return to_chars(buf, sp); +} + +inline size_t to_chars(substr buf, const char * C4_RESTRICT v) noexcept +{ + return to_chars(buf, to_csubstr(v)); +} + +} // namespace c4 + +#ifdef _MSC_VER +# pragma warning(pop) +#elif defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#endif /* _C4_CHARCONV_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/common.hpp b/thirdparty/ryml/ext/c4core/src/c4/common.hpp new file mode 100644 index 000000000..e3063ffc0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/common.hpp @@ -0,0 +1,15 @@ +#ifndef _C4_COMMON_HPP_ +#define _C4_COMMON_HPP_ + +#include "c4/config.hpp" + +#include "c4/preprocessor.hpp" +#include "c4/platform.hpp" +#include "c4/cpu.hpp" +#include "c4/compiler.hpp" +#include "c4/language.hpp" +#include "c4/error.hpp" +#include "c4/time.hpp" +#include "c4/types.hpp" + +#endif /* _C4_COMMON_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/compiler.hpp b/thirdparty/ryml/ext/c4core/src/c4/compiler.hpp new file mode 100644 index 000000000..ba8e5b07c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/compiler.hpp @@ -0,0 +1,117 @@ +#ifndef _C4_COMPILER_HPP_ +#define _C4_COMPILER_HPP_ + +/** @file compiler.hpp Provides compiler information macros + * @ingroup basic_headers */ + +#include "c4/platform.hpp" + +// Compilers: +// C4_MSVC +// Visual Studio 2022: MSVC++ 17, 1930 +// Visual Studio 2019: MSVC++ 16, 1920 +// Visual Studio 2017: MSVC++ 15 +// Visual Studio 2015: MSVC++ 14 +// Visual Studio 2013: MSVC++ 13 +// Visual Studio 2013: MSVC++ 12 +// Visual Studio 2012: MSVC++ 11 +// Visual Studio 2010: MSVC++ 10 +// Visual Studio 2008: MSVC++ 09 +// Visual Studio 2005: MSVC++ 08 +// C4_CLANG +// C4_GCC +// C4_ICC (intel compiler) +/** @see http://sourceforge.net/p/predef/wiki/Compilers/ for a list of compiler identifier macros */ +/** @see https://msdn.microsoft.com/en-us/library/b0084kay.aspx for VS2013 predefined macros */ + +#if defined(_MSC_VER)// && (defined(C4_WIN) || defined(C4_XBOX) || defined(C4_UE4)) +# define C4_MSVC +# define C4_MSVC_VERSION_2022 17 +# define C4_MSVC_VERSION_2019 16 +# define C4_MSVC_VERSION_2017 15 +# define C4_MSVC_VERSION_2015 14 +# define C4_MSVC_VERSION_2013 12 +# define C4_MSVC_VERSION_2012 11 +# if _MSC_VER >= 1930 +# define C4_MSVC_VERSION C4_MSVC_VERSION_2022 // visual studio 2022 +# define C4_MSVC_2022 +# elif _MSC_VER >= 1920 +# define C4_MSVC_VERSION C_4MSVC_VERSION_2019 // visual studio 2019 +# define C4_MSVC_2019 +# elif _MSC_VER >= 1910 +# define C4_MSVC_VERSION C4_MSVC_VERSION_2017 // visual studio 2017 +# define C4_MSVC_2017 +# elif _MSC_VER == 1900 +# define C4_MSVC_VERSION C4_MSVC_VERSION_2015 // visual studio 2015 +# define C4_MSVC_2015 +# elif _MSC_VER == 1800 +# error "MSVC version not supported" +# define C4_MSVC_VERSION C4_MSVC_VERSION_2013 // visual studio 2013 +# define C4_MSVC_2013 +# elif _MSC_VER == 1700 +# error "MSVC version not supported" +# define C4_MSVC_VERSION C4_MSVC_VERSION_2012 // visual studio 2012 +# define C4_MSVC_2012 +# elif _MSC_VER == 1600 +# error "MSVC version not supported" +# define C4_MSVC_VERSION 10 // visual studio 2010 +# define C4_MSVC_2010 +# elif _MSC_VER == 1500 +# error "MSVC version not supported" +# define C4_MSVC_VERSION 09 // visual studio 2008 +# define C4_MSVC_2008 +# elif _MSC_VER == 1400 +# error "MSVC version not supported" +# define C4_MSVC_VERSION 08 // visual studio 2005 +# define C4_MSVC_2005 +# else +# error "MSVC version not supported" +# endif // _MSC_VER +#else +# define C4_MSVC_VERSION 0 // visual studio not present +# define C4_GCC_LIKE +# ifdef __INTEL_COMPILER // check ICC before checking GCC, as ICC defines __GNUC__ too +# define C4_ICC +# define C4_ICC_VERSION __INTEL_COMPILER +# elif defined(__APPLE_CC__) +# define C4_XCODE +# if defined(__clang__) +# define C4_CLANG +# ifndef __apple_build_version__ +# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__) +# else +# define C4_CLANG_VERSION __apple_build_version__ +# endif +# else +# define C4_XCODE_VERSION __APPLE_CC__ +# endif +# elif defined(__clang__) +# define C4_CLANG +# ifndef __apple_build_version__ +# define C4_CLANG_VERSION C4_VERSION_ENCODED(__clang_major__, __clang_minor__, __clang_patchlevel__) +# else +# define C4_CLANG_VERSION __apple_build_version__ +# endif +# elif defined(__GNUC__) +# define C4_GCC +# if defined(__GNUC_PATCHLEVEL__) +# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +# else +# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, 0) +# endif +# if __GNUC__ < 5 +# if __GNUC__ == 4 && __GNUC_MINOR__ >= 8 +// provided by cmake sub-project +# include "c4/gcc-4.8.hpp" +# else +// we do not support GCC < 4.8: +// * misses std::is_trivially_copyable +// * misses std::align +// * -Wshadow has false positives when a local function parameter has the same name as a method +# error "GCC < 4.8 is not supported" +# endif +# endif +# endif +#endif // defined(C4_WIN) && defined(_MSC_VER) + +#endif /* _C4_COMPILER_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/config.hpp b/thirdparty/ryml/ext/c4core/src/c4/config.hpp new file mode 100644 index 000000000..bda8033b3 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/config.hpp @@ -0,0 +1,39 @@ +#ifndef _C4_CONFIG_HPP_ +#define _C4_CONFIG_HPP_ + +/** @defgroup basic_headers Basic headers + * @brief Headers providing basic macros, platform+cpu+compiler information, + * C++ facilities and basic typedefs. */ + +/** @file config.hpp Contains configuration defines and includes the basic_headers. + * @ingroup basic_headers */ + +//#define C4_DEBUG + +#define C4_ERROR_SHOWS_FILELINE +//#define C4_ERROR_SHOWS_FUNC +//#define C4_ERROR_THROWS_EXCEPTION +//#define C4_NO_ALLOC_DEFAULTS +//#define C4_REDEFINE_CPPNEW + +#ifndef C4_SIZE_TYPE +# define C4_SIZE_TYPE size_t +#endif + +#ifndef C4_STR_SIZE_TYPE +# define C4_STR_SIZE_TYPE C4_SIZE_TYPE +#endif + +#ifndef C4_TIME_TYPE +# define C4_TIME_TYPE double +#endif + +#include "c4/export.hpp" +#include "c4/preprocessor.hpp" +#include "c4/platform.hpp" +#include "c4/cpu.hpp" +#include "c4/compiler.hpp" +#include "c4/language.hpp" +#include "c4/types.hpp" + +#endif // _C4_CONFIG_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/cpu.hpp b/thirdparty/ryml/ext/c4core/src/c4/cpu.hpp new file mode 100644 index 000000000..f8877d115 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/cpu.hpp @@ -0,0 +1,139 @@ +#ifndef _C4_CPU_HPP_ +#define _C4_CPU_HPP_ + +/** @file cpu.hpp Provides processor information macros + * @ingroup basic_headers */ + +// see also https://sourceforge.net/p/predef/wiki/Architectures/ +// see also https://sourceforge.net/p/predef/wiki/Endianness/ +// see also https://github.com/googlesamples/android-ndk/blob/android-mk/hello-jni/jni/hello-jni.c +// see http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qprocessordetection.h + +#ifdef __ORDER_LITTLE_ENDIAN__ + #define _C4EL __ORDER_LITTLE_ENDIAN__ +#else + #define _C4EL 1234 +#endif + +#ifdef __ORDER_BIG_ENDIAN__ + #define _C4EB __ORDER_BIG_ENDIAN__ +#else + #define _C4EB 4321 +#endif + +// mixed byte order (eg, PowerPC or ia64) +#define _C4EM 1111 + +#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) + #define C4_CPU_X86_64 + #define C4_WORDSIZE 8 + #define C4_BYTE_ORDER _C4EL + +#elif defined(__i386) || defined(__i386__) || defined(_M_IX86) + #define C4_CPU_X86 + #define C4_WORDSIZE 4 + #define C4_BYTE_ORDER _C4EL + +#elif defined(__arm__) || defined(_M_ARM) \ + || defined(__TARGET_ARCH_ARM) || defined(__aarch64__) || defined(_M_ARM64) + #if defined(__aarch64__) || defined(_M_ARM64) + #define C4_CPU_ARM64 + #define C4_CPU_ARMV8 + #define C4_WORDSIZE 8 + #else + #define C4_CPU_ARM + #define C4_WORDSIZE 4 + #if defined(__ARM_ARCH_8__) || defined(__ARM_ARCH_8A__) \ + || (defined(__ARCH_ARM) && __ARCH_ARM >= 8) + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 8) \ + #define C4_CPU_ARMV8 + #elif defined(__ARM_ARCH_7__) || defined(_ARM_ARCH_7) \ + || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) \ + || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__) \ + || defined(__ARM_ARCH_7EM__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 7) \ + || (defined(_M_ARM) && _M_ARM >= 7) + #define C4_CPU_ARMV7 + #elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6ZK__) \ + || defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_6KZ__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 6) + #define C4_CPU_ARMV6 + #elif defined(__ARM_ARCH_5TEJ__) \ + || defined(__ARM_ARCH_5TE__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5) + #define C4_CPU_ARMV5 + #elif defined(__ARM_ARCH_4T__) \ + || (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 4) + #define C4_CPU_ARMV4 + #else + #error "unknown CPU architecture: ARM" + #endif + #endif + #if defined(__ARMEL__) || defined(__LITTLE_ENDIAN__) || defined(__AARCH64EL__) \ + || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ + || defined(_MSC_VER) // winarm64 does not provide any of the above macros, + // but advises little-endianess: + // https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=msvc-170 + // So if it is visual studio compiling, we'll assume little endian. + #define C4_BYTE_ORDER _C4EL + #elif defined(__ARMEB__) || defined(__BIG_ENDIAN__) || defined(__AARCH64EB__) \ + || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + #define C4_BYTE_ORDER _C4EB + #elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_PDP_ENDIAN__) + #define C4_BYTE_ORDER _C4EM + #else + #error "unknown endianness" + #endif + +#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) + #define C4_CPU_IA64 + #define C4_WORDSIZE 8 + #define C4_BYTE_ORDER _C4EM + // itanium is bi-endian - check byte order below + +#elif defined(__ppc__) || defined(__ppc) || defined(__powerpc__) \ + || defined(_ARCH_COM) || defined(_ARCH_PWR) || defined(_ARCH_PPC) \ + || defined(_M_MPPC) || defined(_M_PPC) + #if defined(__ppc64__) || defined(__powerpc64__) || defined(__64BIT__) + #define C4_CPU_PPC64 + #define C4_WORDSIZE 8 + #else + #define C4_CPU_PPC + #define C4_WORDSIZE 4 + #endif + #define C4_BYTE_ORDER _C4EM + // ppc is bi-endian - check byte order below + +#elif defined(__s390x__) || defined(__zarch__) || defined(__SYSC_ZARCH_) +# define C4_CPU_S390_X +# define C4_WORDSIZE 8 +# define C4_BYTE_ORDER _C4EB + +#elif defined(__riscv) + #if __riscv_xlen == 64 + #define C4_CPU_RISCV64 + #define C4_WORDSIZE 8 + #else + #define C4_CPU_RISCV32 + #define C4_WORDSIZE 4 + #endif + #define C4_BYTE_ORDER _C4EL + +#elif defined(__EMSCRIPTEN__) +# define C4_BYTE_ORDER _C4EL +# define C4_WORDSIZE 4 + +#elif defined(SWIG) + #error "please define CPU architecture macros when compiling with swig" + +#else + #error "unknown CPU architecture" +#endif + +#define C4_LITTLE_ENDIAN (C4_BYTE_ORDER == _C4EL) +#define C4_BIG_ENDIAN (C4_BYTE_ORDER == _C4EB) +#define C4_MIXED_ENDIAN (C4_BYTE_ORDER == _C4EM) + +#endif /* _C4_CPU_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/ctor_dtor.hpp b/thirdparty/ryml/ext/c4core/src/c4/ctor_dtor.hpp new file mode 100644 index 000000000..8624df7b5 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ctor_dtor.hpp @@ -0,0 +1,462 @@ +#ifndef _C4_CTOR_DTOR_HPP_ +#define _C4_CTOR_DTOR_HPP_ + +#include "c4/preprocessor.hpp" +#include "c4/language.hpp" +#include "c4/memory_util.hpp" +#include "c4/error.hpp" + +#include <type_traits> +#include <utility> // std::forward + +/** @file ctor_dtor.hpp object construction and destruction facilities. + * Some of these are not yet available in C++11. */ + +namespace c4 { + +/** default-construct an object, trivial version */ +template <class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_default_constructible<U>::value, void>::type +construct(U *ptr) noexcept +{ + memset(ptr, 0, sizeof(U)); +} +/** default-construct an object, non-trivial version */ +template<class U> C4_ALWAYS_INLINE typename std ::enable_if< ! std::is_trivially_default_constructible<U>::value, void>::type +construct(U* ptr) noexcept +{ + new ((void*)ptr) U(); +} + +/** default-construct n objects, trivial version */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_default_constructible<U>::value, void>::type +construct_n(U* ptr, I n) noexcept +{ + memset(ptr, 0, n * sizeof(U)); +} +/** default-construct n objects, non-trivial version */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_default_constructible<U>::value, void>::type +construct_n(U* ptr, I n) noexcept +{ + for(I i = 0; i < n; ++i) + { + new ((void*)(ptr + i)) U(); + } +} + +#ifdef __clang__ +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# if __GNUC__ >= 6 +# pragma GCC diagnostic ignored "-Wnull-dereference" +# endif +#endif + +template<class U, class ...Args> +inline void construct(U* ptr, Args&&... args) +{ + new ((void*)ptr) U(std::forward<Args>(args)...); +} +template<class U, class I, class ...Args> +inline void construct_n(U* ptr, I n, Args&&... args) +{ + for(I i = 0; i < n; ++i) + { + new ((void*)(ptr + i)) U(args...); + } +} + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + +//----------------------------------------------------------------------------- +// copy-construct + +template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_copy_constructible<U>::value, void>::type +copy_construct(U* dst, U const* src) noexcept +{ + C4_ASSERT(dst != src); + memcpy(dst, src, sizeof(U)); +} +template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_copy_constructible<U>::value, void>::type +copy_construct(U* dst, U const* src) +{ + C4_ASSERT(dst != src); + new ((void*)dst) U(*src); +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_copy_constructible<U>::value, void>::type +copy_construct_n(U* dst, U const* src, I n) noexcept +{ + C4_ASSERT(dst != src); + memcpy(dst, src, n * sizeof(U)); +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_copy_constructible<U>::value, void>::type +copy_construct_n(U* dst, U const* src, I n) +{ + C4_ASSERT(dst != src); + for(I i = 0; i < n; ++i) + { + new ((void*)(dst + i)) U(*(src + i)); + } +} + +template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value, void>::type +copy_construct(U* dst, U src) noexcept // pass by value for scalar types +{ + *dst = src; +} +template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_scalar<U>::value, void>::type +copy_construct(U* dst, U const& src) // pass by reference for non-scalar types +{ + C4_ASSERT(dst != &src); + new ((void*)dst) U(src); +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value, void>::type +copy_construct_n(U* dst, U src, I n) noexcept // pass by value for scalar types +{ + for(I i = 0; i < n; ++i) + { + dst[i] = src; + } +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_scalar<U>::value, void>::type +copy_construct_n(U* dst, U const& src, I n) // pass by reference for non-scalar types +{ + C4_ASSERT(dst != &src); + for(I i = 0; i < n; ++i) + { + new ((void*)(dst + i)) U(src); + } +} + +template<class U, size_t N> +C4_ALWAYS_INLINE void copy_construct(U (&dst)[N], U const (&src)[N]) noexcept +{ + copy_construct_n(dst, src, N); +} + +//----------------------------------------------------------------------------- +// copy-assign + +template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_copy_assignable<U>::value, void>::type +copy_assign(U* dst, U const* src) noexcept +{ + C4_ASSERT(dst != src); + memcpy(dst, src, sizeof(U)); +} +template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_copy_assignable<U>::value, void>::type +copy_assign(U* dst, U const* src) noexcept +{ + C4_ASSERT(dst != src); + *dst = *src; +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_copy_assignable<U>::value, void>::type +copy_assign_n(U* dst, U const* src, I n) noexcept +{ + C4_ASSERT(dst != src); + memcpy(dst, src, n * sizeof(U)); +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_copy_assignable<U>::value, void>::type +copy_assign_n(U* dst, U const* src, I n) noexcept +{ + C4_ASSERT(dst != src); + for(I i = 0; i < n; ++i) + { + dst[i] = src[i]; + } +} + +template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value, void>::type +copy_assign(U* dst, U src) noexcept // pass by value for scalar types +{ + *dst = src; +} +template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_scalar<U>::value, void>::type +copy_assign(U* dst, U const& src) noexcept // pass by reference for non-scalar types +{ + C4_ASSERT(dst != &src); + *dst = src; +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value, void>::type +copy_assign_n(U* dst, U src, I n) noexcept // pass by value for scalar types +{ + for(I i = 0; i < n; ++i) + { + dst[i] = src; + } +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_scalar<U>::value, void>::type +copy_assign_n(U* dst, U const& src, I n) noexcept // pass by reference for non-scalar types +{ + C4_ASSERT(dst != &src); + for(I i = 0; i < n; ++i) + { + dst[i] = src; + } +} + +template<class U, size_t N> +C4_ALWAYS_INLINE void copy_assign(U (&dst)[N], U const (&src)[N]) noexcept +{ + copy_assign_n(dst, src, N); +} + +//----------------------------------------------------------------------------- +// move-construct + +template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type +move_construct(U* dst, U* src) noexcept +{ + C4_ASSERT(dst != src); + memcpy(dst, src, sizeof(U)); +} +template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type +move_construct(U* dst, U* src) noexcept +{ + C4_ASSERT(dst != src); + new ((void*)dst) U(std::move(*src)); +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type +move_construct_n(U* dst, U* src, I n) noexcept +{ + C4_ASSERT(dst != src); + memcpy(dst, src, n * sizeof(U)); +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type +move_construct_n(U* dst, U* src, I n) noexcept +{ + C4_ASSERT(dst != src); + for(I i = 0; i < n; ++i) + { + new ((void*)(dst + i)) U(std::move(src[i])); + } +} + +//----------------------------------------------------------------------------- +// move-assign + +template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_assignable<U>::value, void>::type +move_assign(U* dst, U* src) noexcept +{ + C4_ASSERT(dst != src); + memcpy(dst, src, sizeof(U)); +} +template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_assignable<U>::value, void>::type +move_assign(U* dst, U* src) noexcept +{ + C4_ASSERT(dst != src); + *dst = std::move(*src); +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_assignable<U>::value, void>::type +move_assign_n(U* dst, U* src, I n) noexcept +{ + C4_ASSERT(dst != src); + memcpy(dst, src, n * sizeof(U)); +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_assignable<U>::value, void>::type +move_assign_n(U* dst, U* src, I n) noexcept +{ + C4_ASSERT(dst != src); + for(I i = 0; i < n; ++i) + { + *(dst + i) = std::move(*(src + i)); + } +} + +//----------------------------------------------------------------------------- +// destroy + +template<class U> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_destructible<U>::value, void>::type +destroy(U* ptr) noexcept +{ + C4_UNUSED(ptr); // nothing to do +} +template<class U> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_destructible<U>::value, void>::type +destroy(U* ptr) noexcept +{ + ptr->~U(); +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_destructible<U>::value, void>::type +destroy_n(U* ptr, I n) noexcept +{ + C4_UNUSED(ptr); + C4_UNUSED(n); // nothing to do +} +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_destructible<U>::value, void>::type +destroy_n(U* ptr, I n) noexcept +{ + for(I i = 0; i <n; ++i) + { + ptr[i].~U(); + } +} + +//----------------------------------------------------------------------------- + +/** makes room at the beginning of buf, which has a current size of n */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type +make_room(U *buf, I bufsz, I room) C4_NOEXCEPT_A +{ + C4_ASSERT(bufsz >= 0 && room >= 0); + if(room >= bufsz) + { + memcpy (buf + room, buf, bufsz * sizeof(U)); + } + else + { + memmove(buf + room, buf, bufsz * sizeof(U)); + } +} +/** makes room at the beginning of buf, which has a current size of bufsz */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type +make_room(U *buf, I bufsz, I room) C4_NOEXCEPT_A +{ + C4_ASSERT(bufsz >= 0 && room >= 0); + if(room >= bufsz) + { + for(I i = 0; i < bufsz; ++i) + { + new ((void*)(buf + (i + room))) U(std::move(buf[i])); + } + } + else + { + for(I i = 0; i < bufsz; ++i) + { + I w = bufsz-1 - i; // do a backwards loop + new ((void*)(buf + (w + room))) U(std::move(buf[w])); + } + } +} + +/** make room to the right of pos */ +template<class U, class I> +C4_ALWAYS_INLINE void make_room(U *buf, I bufsz, I currsz, I pos, I room) +{ + C4_ASSERT(pos >= 0 && pos <= currsz); + C4_ASSERT(currsz <= bufsz); + C4_ASSERT(room + currsz <= bufsz); + C4_UNUSED(bufsz); + make_room(buf + pos, currsz - pos, room); +} + + +/** make room to the right of pos, copying to the beginning of a different buffer */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type +make_room(U *dst, U const* src, I srcsz, I room, I pos) C4_NOEXCEPT_A +{ + C4_ASSERT(srcsz >= 0 && room >= 0 && pos >= 0); + C4_ASSERT(pos < srcsz || (pos == 0 && srcsz == 0)); + memcpy(dst , src , pos * sizeof(U)); + memcpy(dst + room + pos, src + pos, (srcsz - pos) * sizeof(U)); +} +/** make room to the right of pos, copying to the beginning of a different buffer */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type +make_room(U *dst, U const* src, I srcsz, I room, I pos) +{ + C4_ASSERT(srcsz >= 0 && room >= 0 && pos >= 0); + C4_ASSERT(pos < srcsz || (pos == 0 && srcsz == 0)); + for(I i = 0; i < pos; ++i) + { + new ((void*)(dst + i)) U(std::move(src[i])); + } + src += pos; + dst += room + pos; + for(I i = 0, e = srcsz - pos; i < e; ++i) + { + new ((void*)(dst + i)) U(std::move(src[i])); + } +} + +template<class U, class I> +C4_ALWAYS_INLINE void make_room +( + U * dst, I dstsz, + U const* src, I srcsz, + I room, I pos +) +{ + C4_ASSERT(pos >= 0 && pos < srcsz || (srcsz == 0 && pos == 0)); + C4_ASSERT(pos >= 0 && pos < dstsz || (dstsz == 0 && pos == 0)); + C4_ASSERT(srcsz+room <= dstsz); + C4_UNUSED(dstsz); + make_room(dst, src, srcsz, room, pos); +} + + +//----------------------------------------------------------------------------- +/** destroy room at the beginning of buf, which has a current size of n */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_scalar<U>::value || (std::is_standard_layout<U>::value && std::is_trivial<U>::value), void>::type +destroy_room(U *buf, I n, I room) C4_NOEXCEPT_A +{ + C4_ASSERT(n >= 0 && room >= 0); + C4_ASSERT(room <= n); + if(room < n) + { + memmove(buf, buf + room, (n - room) * sizeof(U)); + } + else + { + // nothing to do - no need to destroy scalar types + } +} +/** destroy room at the beginning of buf, which has a current size of n */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! (std::is_scalar<U>::value || (std::is_standard_layout<U>::value && std::is_trivial<U>::value)), void>::type +destroy_room(U *buf, I n, I room) +{ + C4_ASSERT(n >= 0 && room >= 0); + C4_ASSERT(room <= n); + if(room < n) + { + for(I i = 0, e = n - room; i < e; ++i) + { + buf[i] = std::move(buf[i + room]); + } + } + else + { + for(I i = 0; i < n; ++i) + { + buf[i].~U(); + } + } +} + +/** destroy room to the right of pos, copying to a different buffer */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if<std::is_trivially_move_constructible<U>::value, void>::type +destroy_room(U *dst, U const* src, I n, I room, I pos) C4_NOEXCEPT_A +{ + C4_ASSERT(n >= 0 && room >= 0 && pos >= 0); + C4_ASSERT(pos <n); + C4_ASSERT(pos + room <= n); + memcpy(dst, src, pos * sizeof(U)); + memcpy(dst + pos, src + room + pos, (n - pos - room) * sizeof(U)); +} +/** destroy room to the right of pos, copying to a different buffer */ +template<class U, class I> C4_ALWAYS_INLINE typename std::enable_if< ! std::is_trivially_move_constructible<U>::value, void>::type +destroy_room(U *dst, U const* src, I n, I room, I pos) +{ + C4_ASSERT(n >= 0 && room >= 0 && pos >= 0); + C4_ASSERT(pos < n); + C4_ASSERT(pos + room <= n); + for(I i = 0; i < pos; ++i) + { + new ((void*)(dst + i)) U(std::move(src[i])); + } + src += room + pos; + dst += pos; + for(I i = 0, e = n - pos - room; i < e; ++i) + { + new ((void*)(dst + i)) U(std::move(src[i])); + } +} + +} // namespace c4 + +#undef _C4REQUIRE + +#endif /* _C4_CTOR_DTOR_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/dump.hpp b/thirdparty/ryml/ext/c4core/src/c4/dump.hpp new file mode 100644 index 000000000..483acf982 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/dump.hpp @@ -0,0 +1,579 @@ +#ifndef C4_DUMP_HPP_ +#define C4_DUMP_HPP_ + +#include <c4/substr.hpp> + +namespace c4 { + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** type of the function to dump characters */ +using DumperPfn = void (*)(csubstr buf); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<DumperPfn dumpfn, class Arg> +inline size_t dump(substr buf, Arg const& a) +{ + size_t sz = to_chars(buf, a); // need to serialize to the buffer + if(C4_LIKELY(sz <= buf.len)) + dumpfn(buf.first(sz)); + return sz; +} + +template<class DumperFn, class Arg> +inline size_t dump(DumperFn &&dumpfn, substr buf, Arg const& a) +{ + size_t sz = to_chars(buf, a); // need to serialize to the buffer + if(C4_LIKELY(sz <= buf.len)) + dumpfn(buf.first(sz)); + return sz; +} + +template<DumperPfn dumpfn> +inline size_t dump(substr buf, csubstr a) +{ + if(buf.len) + dumpfn(a); // dump directly, no need to serialize to the buffer + return 0; // no space was used in the buffer +} + +template<class DumperFn> +inline size_t dump(DumperFn &&dumpfn, substr buf, csubstr a) +{ + if(buf.len) + dumpfn(a); // dump directly, no need to serialize to the buffer + return 0; // no space was used in the buffer +} + +template<DumperPfn dumpfn, size_t N> +inline size_t dump(substr buf, const char (&a)[N]) +{ + if(buf.len) + dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer + return 0; // no space was used in the buffer +} + +template<class DumperFn, size_t N> +inline size_t dump(DumperFn &&dumpfn, substr buf, const char (&a)[N]) +{ + if(buf.len) + dumpfn(csubstr(a)); // dump directly, no need to serialize to the buffer + return 0; // no space was used in the buffer +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** */ +struct DumpResults +{ + enum : size_t { noarg = (size_t)-1 }; + size_t bufsize = 0; + size_t lastok = noarg; + bool success_until(size_t expected) const { return lastok == noarg ? false : lastok >= expected; } + bool write_arg(size_t arg) const { return lastok == noarg || arg > lastok; } + size_t argfail() const { return lastok + 1; } +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/// @cond dev +// terminates the variadic recursion +template<class DumperFn> +size_t cat_dump(DumperFn &&, substr) +{ + return 0; +} + +// terminates the variadic recursion +template<DumperPfn dumpfn> +size_t cat_dump(substr) +{ + return 0; +} +/// @endcond + +/** take the function pointer as a function argument */ +template<class DumperFn, class Arg, class... Args> +size_t cat_dump(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + size_t size_for_a = dump(dumpfn, buf, a); + if(C4_UNLIKELY(size_for_a > buf.len)) + buf = buf.first(0); // ensure no more calls + size_t size_for_more = cat_dump(dumpfn, buf, more...); + return size_for_more > size_for_a ? size_for_more : size_for_a; +} + +/** take the function pointer as a template argument */ +template<DumperPfn dumpfn,class Arg, class... Args> +size_t cat_dump(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + size_t size_for_a = dump<dumpfn>(buf, a); + if(C4_LIKELY(size_for_a > buf.len)) + buf = buf.first(0); // ensure no more calls + size_t size_for_more = cat_dump<dumpfn>(buf, more...); + return size_for_more > size_for_a ? size_for_more : size_for_a; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/// @cond dev +namespace detail { + +// terminates the variadic recursion +template<DumperPfn dumpfn, class Arg> +DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a) +{ + if(C4_LIKELY(results.write_arg(currarg))) + { + size_t sz = dump<dumpfn>(buf, a); // yield to the specialized function + if(currarg == results.lastok + 1 && sz <= buf.len) + results.lastok = currarg; + results.bufsize = sz > results.bufsize ? sz : results.bufsize; + } + return results; +} + +// terminates the variadic recursion +template<class DumperFn, class Arg> +DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a) +{ + if(C4_LIKELY(results.write_arg(currarg))) + { + size_t sz = dump(dumpfn, buf, a); // yield to the specialized function + if(currarg == results.lastok + 1 && sz <= buf.len) + results.lastok = currarg; + results.bufsize = sz > results.bufsize ? sz : results.bufsize; + } + return results; +} + +template<DumperPfn dumpfn, class Arg, class... Args> +DumpResults cat_dump_resume(size_t currarg, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + results = detail::cat_dump_resume<dumpfn>(currarg, results, buf, a); + return detail::cat_dump_resume<dumpfn>(currarg + 1u, results, buf, more...); +} + +template<class DumperFn, class Arg, class... Args> +DumpResults cat_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + results = detail::cat_dump_resume(currarg, dumpfn, results, buf, a); + return detail::cat_dump_resume(currarg + 1u, dumpfn, results, buf, more...); +} +} // namespace detail +/// @endcond + + +template<DumperPfn dumpfn, class Arg, class... Args> +C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + if(results.bufsize > buf.len) + return results; + return detail::cat_dump_resume<dumpfn>(0u, results, buf, a, more...); +} + +template<class DumperFn, class Arg, class... Args> +C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + if(results.bufsize > buf.len) + return results; + return detail::cat_dump_resume(0u, dumpfn, results, buf, a, more...); +} + +template<DumperPfn dumpfn, class Arg, class... Args> +C4_ALWAYS_INLINE DumpResults cat_dump_resume(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + return detail::cat_dump_resume<dumpfn>(0u, DumpResults{}, buf, a, more...); +} + +template<class DumperFn, class Arg, class... Args> +C4_ALWAYS_INLINE DumpResults cat_dump_resume(DumperFn &&dumpfn, substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + return detail::cat_dump_resume(0u, dumpfn, DumpResults{}, buf, a, more...); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/// @cond dev +// terminate the recursion +template<class DumperFn, class Sep> +size_t catsep_dump(DumperFn &&, substr, Sep const& C4_RESTRICT) +{ + return 0; +} + +// terminate the recursion +template<DumperPfn dumpfn, class Sep> +size_t catsep_dump(substr, Sep const& C4_RESTRICT) +{ + return 0; +} +/// @endcond + +/** take the function pointer as a function argument */ +template<class DumperFn, class Sep, class Arg, class... Args> +size_t catsep_dump(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + size_t sz = dump(dumpfn, buf, a); + if(C4_UNLIKELY(sz > buf.len)) + buf = buf.first(0); // ensure no more calls + if C4_IF_CONSTEXPR (sizeof...(more) > 0) + { + size_t szsep = dump(dumpfn, buf, sep); + if(C4_UNLIKELY(szsep > buf.len)) + buf = buf.first(0); // ensure no more calls + sz = sz > szsep ? sz : szsep; + } + size_t size_for_more = catsep_dump(dumpfn, buf, sep, more...); + return size_for_more > sz ? size_for_more : sz; +} + +/** take the function pointer as a template argument */ +template<DumperPfn dumpfn, class Sep, class Arg, class... Args> +size_t catsep_dump(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + size_t sz = dump<dumpfn>(buf, a); + if(C4_UNLIKELY(sz > buf.len)) + buf = buf.first(0); // ensure no more calls + if C4_IF_CONSTEXPR (sizeof...(more) > 0) + { + size_t szsep = dump<dumpfn>(buf, sep); + if(C4_UNLIKELY(szsep > buf.len)) + buf = buf.first(0); // ensure no more calls + sz = sz > szsep ? sz : szsep; + } + size_t size_for_more = catsep_dump<dumpfn>(buf, sep, more...); + return size_for_more > sz ? size_for_more : sz; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/// @cond dev +namespace detail { +template<DumperPfn dumpfn, class Arg> +void catsep_dump_resume_(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a) +{ + if(C4_LIKELY(results->write_arg(currarg))) + { + size_t sz = dump<dumpfn>(*buf, a); + results->bufsize = sz > results->bufsize ? sz : results->bufsize; + if(C4_LIKELY(sz <= buf->len)) + results->lastok = currarg; + else + buf->len = 0; + } +} + +template<class DumperFn, class Arg> +void catsep_dump_resume_(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Arg const& C4_RESTRICT a) +{ + if(C4_LIKELY(results->write_arg(currarg))) + { + size_t sz = dump(dumpfn, *buf, a); + results->bufsize = sz > results->bufsize ? sz : results->bufsize; + if(C4_LIKELY(sz <= buf->len)) + results->lastok = currarg; + else + buf->len = 0; + } +} + +template<DumperPfn dumpfn, class Sep, class Arg> +C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a) +{ + detail::catsep_dump_resume_<dumpfn>(currarg, results, buf, a); +} + +template<class DumperFn, class Sep, class Arg> +C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT, Arg const& C4_RESTRICT a) +{ + detail::catsep_dump_resume_(currarg, dumpfn, results, buf, a); +} + +template<DumperPfn dumpfn, class Sep, class Arg, class... Args> +C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + detail::catsep_dump_resume_<dumpfn>(currarg , results, buf, a); + detail::catsep_dump_resume_<dumpfn>(currarg + 1u, results, buf, sep); + detail::catsep_dump_resume <dumpfn>(currarg + 2u, results, buf, sep, more...); +} + +template<class DumperFn, class Sep, class Arg, class... Args> +C4_ALWAYS_INLINE void catsep_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults *C4_RESTRICT results, substr *C4_RESTRICT buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + detail::catsep_dump_resume_(currarg , dumpfn, results, buf, a); + detail::catsep_dump_resume_(currarg + 1u, dumpfn, results, buf, sep); + detail::catsep_dump_resume (currarg + 2u, dumpfn, results, buf, sep, more...); +} +} // namespace detail +/// @endcond + + +template<DumperPfn dumpfn, class Sep, class... Args> +C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more) +{ + detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...); + return results; +} + +template<class DumperFn, class Sep, class... Args> +C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more) +{ + detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...); + return results; +} + +template<DumperPfn dumpfn, class Sep, class... Args> +C4_ALWAYS_INLINE DumpResults catsep_dump_resume(substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more) +{ + DumpResults results; + detail::catsep_dump_resume<dumpfn>(0u, &results, &buf, sep, more...); + return results; +} + +template<class DumperFn, class Sep, class... Args> +C4_ALWAYS_INLINE DumpResults catsep_dump_resume(DumperFn &&dumpfn, substr buf, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...more) +{ + DumpResults results; + detail::catsep_dump_resume(0u, dumpfn, &results, &buf, sep, more...); + return results; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** take the function pointer as a function argument */ +template<class DumperFn> +C4_ALWAYS_INLINE size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt) +{ + // we can dump without using buf + // but we'll only dump if the buffer is ok + if(C4_LIKELY(buf.len > 0 && fmt.len)) + dumpfn(fmt); + return 0u; +} + +/** take the function pointer as a function argument */ +template<DumperPfn dumpfn> +C4_ALWAYS_INLINE size_t format_dump(substr buf, csubstr fmt) +{ + // we can dump without using buf + // but we'll only dump if the buffer is ok + if(C4_LIKELY(buf.len > 0 && fmt.len > 0)) + dumpfn(fmt); + return 0u; +} + +/** take the function pointer as a function argument */ +template<class DumperFn, class Arg, class... Args> +size_t format_dump(DumperFn &&dumpfn, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + // we can dump without using buf + // but we'll only dump if the buffer is ok + size_t pos = fmt.find("{}"); // @todo use _find_fmt() + if(C4_UNLIKELY(pos == csubstr::npos)) + { + if(C4_LIKELY(buf.len > 0 && fmt.len > 0)) + dumpfn(fmt); + return 0u; + } + if(C4_LIKELY(buf.len > 0 && pos > 0)) + dumpfn(fmt.first(pos)); // we can dump without using buf + fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again + pos = dump(dumpfn, buf, a); + if(C4_UNLIKELY(pos > buf.len)) + buf.len = 0; // ensure no more calls to dump + size_t size_for_more = format_dump(dumpfn, buf, fmt, more...); + return size_for_more > pos ? size_for_more : pos; +} + +/** take the function pointer as a template argument */ +template<DumperPfn dumpfn, class Arg, class... Args> +size_t format_dump(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + // we can dump without using buf + // but we'll only dump if the buffer is ok + size_t pos = fmt.find("{}"); // @todo use _find_fmt() + if(C4_UNLIKELY(pos == csubstr::npos)) + { + if(C4_LIKELY(buf.len > 0 && fmt.len > 0)) + dumpfn(fmt); + return 0u; + } + if(C4_LIKELY(buf.len > 0 && pos > 0)) + dumpfn(fmt.first(pos)); // we can dump without using buf + fmt = fmt.sub(pos + 2); // skip {} do this before assigning to pos again + pos = dump<dumpfn>(buf, a); + if(C4_UNLIKELY(pos > buf.len)) + buf.len = 0; // ensure no more calls to dump + size_t size_for_more = format_dump<dumpfn>(buf, fmt, more...); + return size_for_more > pos ? size_for_more : pos; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/// @cond dev +namespace detail { + +template<DumperPfn dumpfn> +DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt) +{ + // we can dump without using buf + // but we'll only dump if the buffer is ok + if(C4_LIKELY(buf.len > 0)) + { + dumpfn(fmt); + results.lastok = currarg; + } + return results; +} + +template<class DumperFn> +DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt) +{ + // we can dump without using buf + // but we'll only dump if the buffer is ok + if(C4_LIKELY(buf.len > 0)) + { + dumpfn(fmt); + results.lastok = currarg; + } + return results; +} + +template<DumperPfn dumpfn, class Arg, class... Args> +DumpResults format_dump_resume(size_t currarg, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + // we need to process the format even if we're not + // going to print the first arguments because we're resuming + size_t pos = fmt.find("{}"); // @todo use _find_fmt() + // we can dump without using buf + // but we'll only dump if the buffer is ok + if(C4_LIKELY(results.write_arg(currarg))) + { + if(C4_UNLIKELY(pos == csubstr::npos)) + { + if(C4_LIKELY(buf.len > 0)) + { + results.lastok = currarg; + dumpfn(fmt); + } + return results; + } + if(C4_LIKELY(buf.len > 0)) + { + results.lastok = currarg; + dumpfn(fmt.first(pos)); + } + } + fmt = fmt.sub(pos + 2); + if(C4_LIKELY(results.write_arg(currarg + 1))) + { + pos = dump<dumpfn>(buf, a); + results.bufsize = pos > results.bufsize ? pos : results.bufsize; + if(C4_LIKELY(pos <= buf.len)) + results.lastok = currarg + 1; + else + buf.len = 0; + } + return detail::format_dump_resume<dumpfn>(currarg + 2u, results, buf, fmt, more...); +} +/// @endcond + + +template<class DumperFn, class Arg, class... Args> +DumpResults format_dump_resume(size_t currarg, DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + // we need to process the format even if we're not + // going to print the first arguments because we're resuming + size_t pos = fmt.find("{}"); // @todo use _find_fmt() + // we can dump without using buf + // but we'll only dump if the buffer is ok + if(C4_LIKELY(results.write_arg(currarg))) + { + if(C4_UNLIKELY(pos == csubstr::npos)) + { + if(C4_LIKELY(buf.len > 0)) + { + results.lastok = currarg; + dumpfn(fmt); + } + return results; + } + if(C4_LIKELY(buf.len > 0)) + { + results.lastok = currarg; + dumpfn(fmt.first(pos)); + } + } + fmt = fmt.sub(pos + 2); + if(C4_LIKELY(results.write_arg(currarg + 1))) + { + pos = dump(dumpfn, buf, a); + results.bufsize = pos > results.bufsize ? pos : results.bufsize; + if(C4_LIKELY(pos <= buf.len)) + results.lastok = currarg + 1; + else + buf.len = 0; + } + return detail::format_dump_resume(currarg + 2u, dumpfn, results, buf, fmt, more...); +} +} // namespace detail + + +template<DumperPfn dumpfn, class... Args> +C4_ALWAYS_INLINE DumpResults format_dump_resume(DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more) +{ + return detail::format_dump_resume<dumpfn>(0u, results, buf, fmt, more...); +} + +template<class DumperFn, class... Args> +C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, DumpResults results, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more) +{ + return detail::format_dump_resume(0u, dumpfn, results, buf, fmt, more...); +} + + +template<DumperPfn dumpfn, class... Args> +C4_ALWAYS_INLINE DumpResults format_dump_resume(substr buf, csubstr fmt, Args const& C4_RESTRICT ...more) +{ + return detail::format_dump_resume<dumpfn>(0u, DumpResults{}, buf, fmt, more...); +} + +template<class DumperFn, class... Args> +C4_ALWAYS_INLINE DumpResults format_dump_resume(DumperFn &&dumpfn, substr buf, csubstr fmt, Args const& C4_RESTRICT ...more) +{ + return detail::format_dump_resume(0u, dumpfn, DumpResults{}, buf, fmt, more...); +} + + +} // namespace c4 + + +#endif /* C4_DUMP_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/enum.hpp b/thirdparty/ryml/ext/c4core/src/c4/enum.hpp new file mode 100644 index 000000000..785cf5b28 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/enum.hpp @@ -0,0 +1,276 @@ +#ifndef _C4_ENUM_HPP_ +#define _C4_ENUM_HPP_ + +#include "c4/error.hpp" +#include <string.h> + +/** @file enum.hpp utilities for enums: convert to/from string + */ + + +namespace c4 { + +//! taken from http://stackoverflow.com/questions/15586163/c11-type-trait-to-differentiate-between-enum-class-and-regular-enum +template<typename Enum> +using is_scoped_enum = std::integral_constant<bool, std::is_enum<Enum>::value && !std::is_convertible<Enum, int>::value>; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +typedef enum { + EOFFS_NONE = 0, ///< no offset + EOFFS_CLS = 1, ///< get the enum offset for the class name. @see eoffs_cls() + EOFFS_PFX = 2, ///< get the enum offset for the enum prefix. @see eoffs_pfx() + _EOFFS_LAST ///< reserved +} EnumOffsetType; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** A simple (proxy) container for the value-name pairs of an enum type. + * Uses linear search for finds; this could be improved for time-critical + * code. */ +template<class Enum> +class EnumSymbols +{ +public: + + struct Sym + { + Enum value; + const char *name; + + bool cmp(const char *s) const; + bool cmp(const char *s, size_t len) const; + + const char *name_offs(EnumOffsetType t) const; + }; + + using const_iterator = Sym const*; + +public: + + template<size_t N> + EnumSymbols(Sym const (&p)[N]) : m_symbols(p), m_num(N) {} + + size_t size() const { return m_num; } + bool empty() const { return m_num == 0; } + + Sym const* get(Enum v) const { auto p = find(v); C4_CHECK_MSG(p != nullptr, "could not find symbol=%zd", (std::ptrdiff_t)v); return p; } + Sym const* get(const char *s) const { auto p = find(s); C4_CHECK_MSG(p != nullptr, "could not find symbol \"%s\"", s); return p; } + Sym const* get(const char *s, size_t len) const { auto p = find(s, len); C4_CHECK_MSG(p != nullptr, "could not find symbol \"%.*s\"", len, s); return p; } + + Sym const* find(Enum v) const; + Sym const* find(const char *s) const; + Sym const* find(const char *s, size_t len) const; + + Sym const& operator[] (size_t i) const { C4_CHECK(i < m_num); return m_symbols[i]; } + + Sym const* begin() const { return m_symbols; } + Sym const* end () const { return m_symbols + m_num; } + +private: + + Sym const* m_symbols; + size_t const m_num; + +}; + +//----------------------------------------------------------------------------- +/** return an EnumSymbols object for the enum type T + * + * @warning SPECIALIZE! This needs to be specialized for each enum + * type. Failure to provide a specialization will cause a linker + * error. */ +template<class Enum> +EnumSymbols<Enum> const esyms(); + + +/** return the offset for an enum symbol class. For example, + * eoffs_cls<MyEnumClass>() would be 13=strlen("MyEnumClass::"). + * + * With this function you can announce that the full prefix (including + * an eventual enclosing class or C++11 enum class) is of a certain + * length. + * + * @warning Needs to be specialized for each enum class type that + * wants to use this. When no specialization is given, will return + * 0. */ +template<class Enum> +size_t eoffs_cls() +{ + return 0; +} + + +/** return the offset for an enum symbol prefix. This includes + * eoffs_cls(). With this function you can announce that the full + * prefix (including an eventual enclosing class or C++11 enum class + * plus the string prefix) is of a certain length. + * + * @warning Needs to be specialized for each enum class type that + * wants to use this. When no specialization is given, will return + * 0. */ +template<class Enum> +size_t eoffs_pfx() +{ + return 0; +} + + +template<class Enum> +size_t eoffs(EnumOffsetType which) +{ + switch(which) + { + case EOFFS_NONE: + return 0; + case EOFFS_CLS: + return eoffs_cls<Enum>(); + case EOFFS_PFX: + { + size_t pfx = eoffs_pfx<Enum>(); + return pfx > 0 ? pfx : eoffs_cls<Enum>(); + } + default: + C4_ERROR("unknown offset type %d", (int)which); + return 0; + } +} + + +//----------------------------------------------------------------------------- +/** get the enum value corresponding to a c-string */ + +#ifdef __clang__ +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# if __GNUC__ >= 6 +# pragma GCC diagnostic ignored "-Wnull-dereference" +# endif +#endif + +template<class Enum> +Enum str2e(const char* str) +{ + auto pairs = esyms<Enum>(); + auto *p = pairs.get(str); + C4_CHECK_MSG(p != nullptr, "no valid enum pair name for '%s'", str); + return p->value; +} + +/** get the c-string corresponding to an enum value */ +template<class Enum> +const char* e2str(Enum e) +{ + auto es = esyms<Enum>(); + auto *p = es.get(e); + C4_CHECK_MSG(p != nullptr, "no valid enum pair name"); + return p->name; +} + +/** like e2str(), but add an offset. */ +template<class Enum> +const char* e2stroffs(Enum e, EnumOffsetType ot=EOFFS_PFX) +{ + const char *s = e2str<Enum>(e) + eoffs<Enum>(ot); + return s; +} + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +//----------------------------------------------------------------------------- +/** Find a symbol by value. Returns nullptr when none is found */ +template<class Enum> +typename EnumSymbols<Enum>::Sym const* EnumSymbols<Enum>::find(Enum v) const +{ + for(Sym const* p = this->m_symbols, *e = p+this->m_num; p < e; ++p) + if(p->value == v) + return p; + return nullptr; +} + +/** Find a symbol by name. Returns nullptr when none is found */ +template<class Enum> +typename EnumSymbols<Enum>::Sym const* EnumSymbols<Enum>::find(const char *s) const +{ + for(Sym const* p = this->m_symbols, *e = p+this->m_num; p < e; ++p) + if(p->cmp(s)) + return p; + return nullptr; +} + +/** Find a symbol by name. Returns nullptr when none is found */ +template<class Enum> +typename EnumSymbols<Enum>::Sym const* EnumSymbols<Enum>::find(const char *s, size_t len) const +{ + for(Sym const* p = this->m_symbols, *e = p+this->m_num; p < e; ++p) + if(p->cmp(s, len)) + return p; + return nullptr; +} + +//----------------------------------------------------------------------------- +template<class Enum> +bool EnumSymbols<Enum>::Sym::cmp(const char *s) const +{ + if(strcmp(name, s) == 0) + return true; + + for(int i = 1; i < _EOFFS_LAST; ++i) + { + auto o = eoffs<Enum>((EnumOffsetType)i); + if(o > 0) + if(strcmp(name + o, s) == 0) + return true; + } + + return false; +} + +template<class Enum> +bool EnumSymbols<Enum>::Sym::cmp(const char *s, size_t len) const +{ + if(strncmp(name, s, len) == 0) + return true; + + size_t nlen = 0; + for(int i = 1; i <_EOFFS_LAST; ++i) + { + auto o = eoffs<Enum>((EnumOffsetType)i); + if(o > 0) + { + if(!nlen) + { + nlen = strlen(name); + } + C4_ASSERT(o < nlen); + size_t rem = nlen - o; + auto m = len > rem ? len : rem; + if(len >= m && strncmp(name + o, s, m) == 0) + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +template<class Enum> +const char* EnumSymbols<Enum>::Sym::name_offs(EnumOffsetType t) const +{ + C4_ASSERT(eoffs<Enum>(t) < strlen(name)); + return name + eoffs<Enum>(t); +} + +} // namespace c4 + +#endif // _C4_ENUM_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/error.cpp b/thirdparty/ryml/ext/c4core/src/c4/error.cpp new file mode 100644 index 000000000..ec2d2f81e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/error.cpp @@ -0,0 +1,227 @@ +#include "c4/error.hpp" + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> + +#define C4_LOGF_ERR(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) +#define C4_LOGF_WARN(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) +#define C4_LOGP(msg, ...) printf(msg) + +#if defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC)) +# include "c4/windows.hpp" +#elif defined(C4_PS4) +# include <libdbg.h> +#elif defined(C4_UNIX) || defined(C4_LINUX) +# include <sys/stat.h> +# include <cstring> +# include <fcntl.h> +#elif defined(C4_MACOS) || defined(C4_IOS) +# include <assert.h> +# include <stdbool.h> +# include <sys/types.h> +# include <sys/sysctl.h> +#endif +// the amalgamation tool is dumb and was omitting this include under MACOS. +// So do it only once: +#if defined(C4_UNIX) || defined(C4_LINUX) || defined(C4_MACOS) || defined(C4_IOS) +# include <unistd.h> +#endif + +#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION) +# include <exception> +#endif + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wformat-nonliteral" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + + +//----------------------------------------------------------------------------- +namespace c4 { + +static error_flags s_error_flags = ON_ERROR_DEFAULTS; +static error_callback_type s_error_callback = nullptr; + +//----------------------------------------------------------------------------- + +error_flags get_error_flags() +{ + return s_error_flags; +} +void set_error_flags(error_flags flags) +{ + s_error_flags = flags; +} + +error_callback_type get_error_callback() +{ + return s_error_callback; +} +/** Set the function which is called when an error occurs. */ +void set_error_callback(error_callback_type cb) +{ + s_error_callback = cb; +} + +//----------------------------------------------------------------------------- + +void handle_error(srcloc where, const char *fmt, ...) +{ + char buf[1024]; + size_t msglen = 0; + if(s_error_flags & (ON_ERROR_LOG|ON_ERROR_CALLBACK)) + { + va_list args; + va_start(args, fmt); + int ilen = vsnprintf(buf, sizeof(buf), fmt, args); // ss.vprintf(fmt, args); + va_end(args); + msglen = ilen >= 0 && ilen < (int)sizeof(buf) ? static_cast<size_t>(ilen) : sizeof(buf)-1; + } + + if(s_error_flags & ON_ERROR_LOG) + { + C4_LOGF_ERR("\n"); +#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC) + C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf); + C4_LOGF_ERR("%s:%d: ERROR here: %s\n", where.file, where.line, where.func); +#elif defined(C4_ERROR_SHOWS_FILELINE) + C4_LOGF_ERR("%s:%d: ERROR: %s\n", where.file, where.line, buf); +#elif ! defined(C4_ERROR_SHOWS_FUNC) + C4_LOGF_ERR("ERROR: %s\n", buf); +#endif + } + + if(s_error_flags & ON_ERROR_CALLBACK) + { + if(s_error_callback) + { + s_error_callback(buf, msglen/*ss.c_strp(), ss.tellp()*/); + } + } + + if(s_error_flags & ON_ERROR_ABORT) + { + abort(); + } + + if(s_error_flags & ON_ERROR_THROW) + { +#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION) + throw Exception(buf); +#else + abort(); +#endif + } +} + +//----------------------------------------------------------------------------- + +void handle_warning(srcloc where, const char *fmt, ...) +{ + va_list args; + char buf[1024]; //sstream<c4::string> ss; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + C4_LOGF_WARN("\n"); +#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC) + C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/); + C4_LOGF_WARN("%s:%d: WARNING: here: %s\n", where.file, where.line, where.func); +#elif defined(C4_ERROR_SHOWS_FILELINE) + C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/); +#elif ! defined(C4_ERROR_SHOWS_FUNC) + C4_LOGF_WARN("WARNING: %s\n", buf/*ss.c_strp()*/); +#endif + //c4::log.flush(); +} + +//----------------------------------------------------------------------------- +bool is_debugger_attached() +{ +#if defined(C4_UNIX) || defined(C4_LINUX) + static bool first_call = true; + static bool first_call_result = false; + if(first_call) + { + first_call = false; + //! @see http://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb + //! (this answer: http://stackoverflow.com/a/24969863/3968589 ) + char buf[1024] = ""; + + int status_fd = open("/proc/self/status", O_RDONLY); + if (status_fd == -1) + { + return 0; + } + + ssize_t num_read = ::read(status_fd, buf, sizeof(buf)); + + if (num_read > 0) + { + static const char TracerPid[] = "TracerPid:"; + char *tracer_pid; + + if(num_read < 1024) + { + buf[num_read] = 0; + } + tracer_pid = strstr(buf, TracerPid); + if (tracer_pid) + { + first_call_result = !!::atoi(tracer_pid + sizeof(TracerPid) - 1); + } + } + } + return first_call_result; +#elif defined(C4_PS4) + return (sceDbgIsDebuggerAttached() != 0); +#elif defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC)) + return IsDebuggerPresent() != 0; +#elif defined(C4_MACOS) || defined(C4_IOS) + // https://stackoverflow.com/questions/2200277/detecting-debugger-on-mac-os-x + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + assert(junk == 0); + + // We're being debugged if the P_TRACED flag is set. + return ((info.kp_proc.p_flag & P_TRACED) != 0); +#else + return false; +#endif +} // is_debugger_attached() + +} // namespace c4 + + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/error.hpp b/thirdparty/ryml/ext/c4core/src/c4/error.hpp new file mode 100644 index 000000000..26e457bfa --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/error.hpp @@ -0,0 +1,432 @@ +#ifndef _C4_ERROR_HPP_ +#define _C4_ERROR_HPP_ + +/** @file error.hpp Facilities for error reporting and runtime assertions. */ + +/** @defgroup error_checking Error checking */ + +#include "c4/config.hpp" + +#ifdef _DOXYGEN_ + /** if this is defined and exceptions are enabled, then calls to C4_ERROR() + * will throw an exception + * @ingroup error_checking */ +# define C4_EXCEPTIONS_ENABLED + /** if this is defined and exceptions are enabled, then calls to C4_ERROR() + * will throw an exception + * @see C4_EXCEPTIONS_ENABLED + * @ingroup error_checking */ +# define C4_ERROR_THROWS_EXCEPTION + /** evaluates to noexcept when C4_ERROR might be called and + * exceptions are disabled. Otherwise, defaults to nothing. + * @ingroup error_checking */ +# define C4_NOEXCEPT +#endif // _DOXYGEN_ + +#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION) +# define C4_NOEXCEPT +#else +# define C4_NOEXCEPT noexcept +#endif + + +namespace c4 { +namespace detail { +struct fail_type__ {}; +} // detail +} // c4 +#define C4_STATIC_ERROR(dummy_type, errmsg) \ + static_assert(std::is_same<dummy_type, c4::detail::fail_type__>::value, errmsg) + + +//----------------------------------------------------------------------------- + +#define C4_ASSERT_SAME_TYPE(ty1, ty2) \ + C4_STATIC_ASSERT(std::is_same<ty1 C4_COMMA_X ty2>::value) + +#define C4_ASSERT_DIFF_TYPE(ty1, ty2) \ + C4_STATIC_ASSERT( ! std::is_same<ty1 C4_COMMA_X ty2>::value) + + +//----------------------------------------------------------------------------- + +#ifdef _DOXYGEN_ +/** utility macro that triggers a breakpoint when + * the debugger is attached and NDEBUG is not defined. + * @ingroup error_checking */ +# define C4_DEBUG_BREAK() +#endif // _DOXYGEN_ + + +#if defined(NDEBUG) || defined(C4_NO_DEBUG_BREAK) +# define C4_DEBUG_BREAK() +#else +# ifdef __clang__ +# pragma clang diagnostic push +# if !defined(__APPLE_CC__) +# if __clang_major__ >= 10 +# pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern] +# endif +# else +# if __clang_major__ >= 13 +# pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern] +# endif +# endif +# elif defined(__GNUC__) +# endif +# include <c4/ext/debugbreak/debugbreak.h> +# define C4_DEBUG_BREAK() if(c4::is_debugger_attached()) { ::debug_break(); } +# ifdef __clang__ +# pragma clang diagnostic pop +# elif defined(__GNUC__) +# endif +#endif + +namespace c4 { +C4CORE_EXPORT bool is_debugger_attached(); +} // namespace c4 + + +//----------------------------------------------------------------------------- + +#ifdef __clang__ + /* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to + * variadic macros is not portable, but works in clang, gcc, msvc, icc. + * clang requires switching off compiler warnings for pedantic mode. + * @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension +#elif defined(__GNUC__) + /* GCC also issues a warning for zero-args calls to variadic macros. + * This warning is switched on with -pedantic and apparently there is no + * easy way to turn it off as with clang. But marking this as a system + * header works. + * @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html + * @see http://stackoverflow.com/questions/35587137/ */ +# pragma GCC system_header +#endif + + +//----------------------------------------------------------------------------- + +namespace c4 { + +typedef enum : uint32_t { + /** when an error happens and the debugger is attached, call C4_DEBUG_BREAK(). + * Without effect otherwise. */ + ON_ERROR_DEBUGBREAK = 0x01 << 0, + /** when an error happens log a message. */ + ON_ERROR_LOG = 0x01 << 1, + /** when an error happens invoke a callback if it was set with + * set_error_callback(). */ + ON_ERROR_CALLBACK = 0x01 << 2, + /** when an error happens call std::terminate(). */ + ON_ERROR_ABORT = 0x01 << 3, + /** when an error happens and exceptions are enabled throw an exception. + * Without effect otherwise. */ + ON_ERROR_THROW = 0x01 << 4, + /** the default flags. */ + ON_ERROR_DEFAULTS = ON_ERROR_DEBUGBREAK|ON_ERROR_LOG|ON_ERROR_CALLBACK|ON_ERROR_ABORT +} ErrorFlags_e; +using error_flags = uint32_t; +C4CORE_EXPORT void set_error_flags(error_flags f); +C4CORE_EXPORT error_flags get_error_flags(); + + +using error_callback_type = void (*)(const char* msg, size_t msg_size); +C4CORE_EXPORT void set_error_callback(error_callback_type cb); +C4CORE_EXPORT error_callback_type get_error_callback(); + + +//----------------------------------------------------------------------------- +/** RAII class controling the error settings inside a scope. */ +struct ScopedErrorSettings +{ + error_flags m_flags; + error_callback_type m_callback; + + explicit ScopedErrorSettings(error_callback_type cb) + : m_flags(get_error_flags()), + m_callback(get_error_callback()) + { + set_error_callback(cb); + } + explicit ScopedErrorSettings(error_flags flags) + : m_flags(get_error_flags()), + m_callback(get_error_callback()) + { + set_error_flags(flags); + } + explicit ScopedErrorSettings(error_flags flags, error_callback_type cb) + : m_flags(get_error_flags()), + m_callback(get_error_callback()) + { + set_error_flags(flags); + set_error_callback(cb); + } + ~ScopedErrorSettings() + { + set_error_flags(m_flags); + set_error_callback(m_callback); + } +}; + + +//----------------------------------------------------------------------------- + +/** source location */ +struct srcloc; + +C4CORE_EXPORT void handle_error(srcloc s, const char *fmt, ...); +C4CORE_EXPORT void handle_warning(srcloc s, const char *fmt, ...); + + +# define C4_ERROR(msg, ...) \ + do { \ + if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \ + { \ + C4_DEBUG_BREAK() \ + } \ + c4::handle_error(C4_SRCLOC(), msg, ## __VA_ARGS__); \ + } while(0) + + +# define C4_WARNING(msg, ...) \ + c4::handle_warning(C4_SRCLOC(), msg, ## __VA_ARGS__) + + +#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC) + +struct srcloc +{ + const char *file = ""; + const char *func = ""; + int line = 0; +}; +#define C4_SRCLOC() c4::srcloc{__FILE__, C4_PRETTY_FUNC, __LINE__} + +#elif defined(C4_ERROR_SHOWS_FILELINE) + +struct srcloc +{ + const char *file; + int line; +}; +#define C4_SRCLOC() c4::srcloc{__FILE__, __LINE__} + +#elif ! defined(C4_ERROR_SHOWS_FUNC) + +struct srcloc +{ +}; +#define C4_SRCLOC() c4::srcloc() + +#else +# error not implemented +#endif + + +//----------------------------------------------------------------------------- +// assertions + +// Doxygen needs this so that only one definition counts +#ifdef _DOXYGEN_ + /** Explicitly enables assertions, independently of NDEBUG status. + * This is meant to allow enabling assertions even when NDEBUG is defined. + * Defaults to undefined. + * @ingroup error_checking */ +# define C4_USE_ASSERT + /** assert that a condition is true; this is turned off when NDEBUG + * is defined and C4_USE_ASSERT is not true. + * @ingroup error_checking */ +# define C4_ASSERT + /** same as C4_ASSERT(), additionally prints a printf-formatted message + * @ingroup error_checking */ +# define C4_ASSERT_MSG + /** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults + * to noexcept + * @ingroup error_checking */ +# define C4_NOEXCEPT_A +#endif // _DOXYGEN_ + +#ifndef C4_USE_ASSERT +# ifdef NDEBUG +# define C4_USE_ASSERT 0 +# else +# define C4_USE_ASSERT 1 +# endif +#endif + +#if C4_USE_ASSERT +# define C4_ASSERT(cond) C4_CHECK(cond) +# define C4_ASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__) +# define C4_ASSERT_IF(predicate, cond) if(predicate) { C4_ASSERT(cond); } +# define C4_NOEXCEPT_A C4_NOEXCEPT +#else +# define C4_ASSERT(cond) +# define C4_ASSERT_MSG(cond, /*fmt, */...) +# define C4_ASSERT_IF(predicate, cond) +# define C4_NOEXCEPT_A noexcept +#endif + + +//----------------------------------------------------------------------------- +// extreme assertions + +// Doxygen needs this so that only one definition counts +#ifdef _DOXYGEN_ + /** Explicitly enables extreme assertions; this is meant to allow enabling + * assertions even when NDEBUG is defined. Defaults to undefined. + * @ingroup error_checking */ +# define C4_USE_XASSERT + /** extreme assertion: can be switched off independently of + * the regular assertion; use for example for bounds checking in hot code. + * Turned on only when C4_USE_XASSERT is defined + * @ingroup error_checking */ +# define C4_XASSERT + /** same as C4_XASSERT(), and additionally prints a printf-formatted message + * @ingroup error_checking */ +# define C4_XASSERT_MSG + /** evaluates to C4_NOEXCEPT when C4_XASSERT is disabled; otherwise, defaults to noexcept + * @ingroup error_checking */ +# define C4_NOEXCEPT_X +#endif // _DOXYGEN_ + +#ifndef C4_USE_XASSERT +# define C4_USE_XASSERT C4_USE_ASSERT +#endif + +#if C4_USE_XASSERT +# define C4_XASSERT(cond) C4_CHECK(cond) +# define C4_XASSERT_MSG(cond, /*fmt, */...) C4_CHECK_MSG(cond, ## __VA_ARGS__) +# define C4_XASSERT_IF(predicate, cond) if(predicate) { C4_XASSERT(cond); } +# define C4_NOEXCEPT_X C4_NOEXCEPT +#else +# define C4_XASSERT(cond) +# define C4_XASSERT_MSG(cond, /*fmt, */...) +# define C4_XASSERT_IF(predicate, cond) +# define C4_NOEXCEPT_X noexcept +#endif + + +//----------------------------------------------------------------------------- +// checks: never switched-off + +/** Check that a condition is true, or raise an error when not + * true. Unlike C4_ASSERT(), this check is not disabled in non-debug + * builds. + * @see C4_ASSERT + * @ingroup error_checking + * + * @todo add constexpr-compatible compile-time assert: + * https://akrzemi1.wordpress.com/2017/05/18/asserts-in-constexpr-functions/ + */ +#define C4_CHECK(cond) \ + do { \ + if(C4_UNLIKELY(!(cond))) \ + { \ + C4_ERROR("check failed: %s", #cond); \ + } \ + } while(0) + + +/** like C4_CHECK(), and additionally log a printf-style message. + * @see C4_CHECK + * @ingroup error_checking */ +#define C4_CHECK_MSG(cond, fmt, ...) \ + do { \ + if(C4_UNLIKELY(!(cond))) \ + { \ + C4_ERROR("check failed: " #cond "\n" fmt, ## __VA_ARGS__); \ + } \ + } while(0) + + +//----------------------------------------------------------------------------- +// Common error conditions + +#define C4_NOT_IMPLEMENTED() C4_ERROR("NOT IMPLEMENTED") +#define C4_NOT_IMPLEMENTED_MSG(/*msg, */...) C4_ERROR("NOT IMPLEMENTED: " ## __VA_ARGS__) +#define C4_NOT_IMPLEMENTED_IF(condition) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED"); } } while(0) +#define C4_NOT_IMPLEMENTED_IF_MSG(condition, /*msg, */...) do { if(C4_UNLIKELY(condition)) { C4_ERROR("NOT IMPLEMENTED: " ## __VA_ARGS__); } } while(0) + +#define C4_NEVER_REACH() do { C4_ERROR("never reach this point"); C4_UNREACHABLE(); } while(0) +#define C4_NEVER_REACH_MSG(/*msg, */...) do { C4_ERROR("never reach this point: " ## __VA_ARGS__); C4_UNREACHABLE(); } while(0) + + + +//----------------------------------------------------------------------------- +// helpers for warning suppression +// idea adapted from https://github.com/onqtam/doctest/ + + +#ifdef C4_MSVC +#define C4_SUPPRESS_WARNING_MSVC_PUSH __pragma(warning(push)) +#define C4_SUPPRESS_WARNING_MSVC(w) __pragma(warning(disable : w)) +#define C4_SUPPRESS_WARNING_MSVC_POP __pragma(warning(pop)) +#define C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(w) \ + C4_SUPPRESS_WARNING_MSVC_PUSH \ + C4_SUPPRESS_WARNING_MSVC(w) +#else // C4_MSVC +#define C4_SUPPRESS_WARNING_MSVC_PUSH +#define C4_SUPPRESS_WARNING_MSVC(w) +#define C4_SUPPRESS_WARNING_MSVC_POP +#define C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(w) +#endif // C4_MSVC + + +#ifdef C4_CLANG +#define C4_PRAGMA_TO_STR(x) _Pragma(#x) +#define C4_SUPPRESS_WARNING_CLANG_PUSH _Pragma("clang diagnostic push") +#define C4_SUPPRESS_WARNING_CLANG(w) C4_PRAGMA_TO_STR(clang diagnostic ignored w) +#define C4_SUPPRESS_WARNING_CLANG_POP _Pragma("clang diagnostic pop") +#define C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w) \ + C4_SUPPRESS_WARNING_CLANG_PUSH \ + C4_SUPPRESS_WARNING_CLANG(w) +#else // C4_CLANG +#define C4_SUPPRESS_WARNING_CLANG_PUSH +#define C4_SUPPRESS_WARNING_CLANG(w) +#define C4_SUPPRESS_WARNING_CLANG_POP +#define C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w) +#endif // C4_CLANG + + +#ifdef C4_GCC +#define C4_PRAGMA_TO_STR(x) _Pragma(#x) +#define C4_SUPPRESS_WARNING_GCC_PUSH _Pragma("GCC diagnostic push") +#define C4_SUPPRESS_WARNING_GCC(w) C4_PRAGMA_TO_STR(GCC diagnostic ignored w) +#define C4_SUPPRESS_WARNING_GCC_POP _Pragma("GCC diagnostic pop") +#define C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \ + C4_SUPPRESS_WARNING_GCC_PUSH \ + C4_SUPPRESS_WARNING_GCC(w) +#else // C4_GCC +#define C4_SUPPRESS_WARNING_GCC_PUSH +#define C4_SUPPRESS_WARNING_GCC(w) +#define C4_SUPPRESS_WARNING_GCC_POP +#define C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) +#endif // C4_GCC + + +#define C4_SUPPRESS_WARNING_GCC_CLANG_PUSH \ + C4_SUPPRESS_WARNING_GCC_PUSH \ + C4_SUPPRESS_WARNING_CLANG_PUSH + +#define C4_SUPPRESS_WARNING_GCC_CLANG(w) \ + C4_SUPPRESS_WARNING_GCC(w) \ + C4_SUPPRESS_WARNING_CLANG(w) + +#define C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH(w) \ + C4_SUPPRESS_WARNING_GCC_WITH_PUSH(w) \ + C4_SUPPRESS_WARNING_CLANG_WITH_PUSH(w) + +#define C4_SUPPRESS_WARNING_GCC_CLANG_POP \ + C4_SUPPRESS_WARNING_GCC_POP \ + C4_SUPPRESS_WARNING_CLANG_POP + +} // namespace c4 + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#endif /* _C4_ERROR_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/export.hpp b/thirdparty/ryml/ext/c4core/src/c4/export.hpp new file mode 100644 index 000000000..ffd02482f --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/export.hpp @@ -0,0 +1,18 @@ +#ifndef C4_EXPORT_HPP_ +#define C4_EXPORT_HPP_ + +#ifdef _WIN32 + #ifdef C4CORE_SHARED + #ifdef C4CORE_EXPORTS + #define C4CORE_EXPORT __declspec(dllexport) + #else + #define C4CORE_EXPORT __declspec(dllimport) + #endif + #else + #define C4CORE_EXPORT + #endif +#else + #define C4CORE_EXPORT +#endif + +#endif /* C4CORE_EXPORT_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/.gitignore b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/.gitignore new file mode 100644 index 000000000..e44cb5a54 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/.gitignore @@ -0,0 +1,10 @@ +*.o +fib +core +core.* +cscope.out +tags +.gdb_history +test/trap +test/break +test/break-c++ diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/COPYING b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/COPYING new file mode 100644 index 000000000..9e51088a5 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/COPYING @@ -0,0 +1,23 @@ +Copyright (c) 2011-2016, Scott Tsai + +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. + +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/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/GNUmakefile b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/GNUmakefile new file mode 100644 index 000000000..17ab01f9b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/GNUmakefile @@ -0,0 +1,28 @@ +CFLAGS := -Os -Wall -g +CXXFLAGS := $(CFLAGS) + +PROGRAMS := $(basename $(wildcard *.c test/*.c test/*.cc *.S)) + +.PHONY: all clean +all: $(PROGRAMS) +clean: + rm -f $(PROGRAMS) cscope.out tags + +%: %.S + $(CC) $(CFLAGS) -nostdlib $< -o $@ + +# Not using builtin rules due to debugbreak.h dependency +%: %.c + $(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ + +%: %.cc + $(CXX) $(CXXFLAGS) $(LDFLAGS) $< -o $@ + +test/%: CFLAGS +=-I. +test/%: CXXFLAGS +=-I. +$(PROGRAMS): debugbreak.h + +GDB ?= gdb +.PHONY: gdb +gdb: + $(GDB) -q -x debugbreak-gdb.py diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/HOW-TO-USE-DEBUGBREAK-GDB-PY.md b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/HOW-TO-USE-DEBUGBREAK-GDB-PY.md new file mode 100644 index 000000000..d5295c50c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/HOW-TO-USE-DEBUGBREAK-GDB-PY.md @@ -0,0 +1,33 @@ +# How to use `debugbreak-gdb.py` + +Just add `-x debugbreak-gdb.py` to your usual GDB invocation or add `source debugbreak-gdb.py` to your `$HOME/.gdbinit`. + +Here's a sample session: + +``` +$ cd debugbreak +$ make +$ gdb -q -x debugbreak-gdb.py test/break +Reading symbols from test/break...done. + +(gdb) set disassemble-next-line 1 +(gdb) run +Starting program: /home/fedora/debugbreak/test/break + +Program received signal SIGTRAP, Trace/breakpoint trap. +main () at test/break.c:6 +6 debug_break(); + +Program received signal SIGTRAP, Trace/breakpoint trap. +main () at test/break.c:6 +6 debug_break(); +(gdb) debugbreak-step +7 printf("hello world\n"); +(gdb) debugbreak-continue +hello world +[Inferior 1 (process 12533) exited normally] +(gdb) + +``` + +On ARM and POWER, trying to use `step` or `stepi` in place of `debugbreak-step` in the sesion above wouldn't have worked as execution would be stock on the breakpoint instruction. diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/README.md b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/README.md new file mode 100644 index 000000000..965092c08 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/README.md @@ -0,0 +1,127 @@ +# Debug Break + +[debugbreak.h](https://github.com/scottt/debugbreak/blob/master/debugbreak.h) allows you to put breakpoints in your C/C++ code with a call to **debug_break()**: +```C +#include <stdio.h> +#include "debugbreak.h" + +int main() +{ + debug_break(); /* will break into debugger */ + printf("hello world\n"); + return 0; +} +``` +* Include one header file and insert calls to `debug_break()` in the code where you wish to break into the debugger. +* Supports GCC, Clang and MSVC. +* Works well on ARM, AArch64, i686, x86-64, POWER and has a fallback code path for other architectures. +* Works like the **DebugBreak()** fuction provided by [Windows](http://msdn.microsoft.com/en-us/library/ea9yy3ey.aspx) and [QNX](http://www.qnx.com/developers/docs/6.3.0SP3/neutrino/lib_ref/d/debugbreak.html). + +**License**: the very permissive [2-Clause BSD](https://github.com/scottt/debugbreak/blob/master/COPYING). + +Known Problem: if continuing execution after a debugbreak breakpoint hit doesn't work (e.g. on ARM or POWER), see [HOW-TO-USE-DEBUGBREAK-GDB-PY.md](HOW-TO-USE-DEBUGBREAK-GDB-PY.md) for a workaround. + +Implementation Notes +================================ + +The requirements for the **debug_break()** function are: +* Act as a compiler code motion barrier +* Don't cause the compiler optimizers to think the code following it can be removed +* Trigger a software breakpoint hit when executed (e.g. **SIGTRAP** on Linux) +* GDB commands like **continue**, **next**, **step**, **stepi** must work after a **debug_break()** hit + +Ideally, both GCC and Clang would provide a **__builtin_debugtrap()** that satisfies the above on all architectures and operating systems. Unfortunately, that is not the case (yet). +GCC's [__builtin_trap()](http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#index-g_t_005f_005fbuiltin_005ftrap-3278) causes the optimizers to think the code follwing can be removed ([test/trap.c](https://github.com/scottt/debugbreak/blob/master/test/trap.c)): +```C +#include <stdio.h> + +int main() +{ + __builtin_trap(); + printf("hello world\n"); + return 0; +} +``` +compiles to: +``` +main +0x0000000000400390 <+0>: 0f 0b ud2 +``` +Notice how the call to `printf()` is not present in the assembly output. + +Further, on i386 / x86-64 **__builtin_trap()** generates an **ud2** instruction which triggers **SIGILL** instead of **SIGTRAP**. This makes it necessary to change GDB's default behavior on **SIGILL** to not terminate the process being debugged: +``` +(gdb) handle SIGILL stop nopass +``` +Even after this, continuing execution in GDB doesn't work well on some GCC, GDB combinations. See [GCC Bugzilla 84595](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84595). + +On ARM, **__builtin_trap()** generates a call to **abort()**, making it even less suitable. + +**debug_break()** generates an **int3** instruction on i386 / x86-64 ([test/break.c](https://github.com/scottt/debugbreak/blob/master/test/break.c)): +```C +#include <stdio.h> +#include "debugbreak.h" + +int main() +{ + debug_break(); + printf("hello world\n"); + return 0; +} +``` +compiles to: +``` +main +0x00000000004003d0 <+0>: 50 push %rax +0x00000000004003d1 <+1>: cc int3 +0x00000000004003d2 <+2>: bf a0 05 40 00 mov $0x4005a0,%edi +0x00000000004003d7 <+7>: e8 d4 ff ff ff callq 0x4003b0 <puts@plt> +0x00000000004003dc <+12>: 31 c0 xor %eax,%eax +0x00000000004003de <+14>: 5a pop %rdx +0x00000000004003df <+15>: c3 retq +``` +which correctly trigges **SIGTRAP** and single-stepping in GDB after a **debug_break()** hit works well. + +Clang / LLVM also has a **__builtin_trap()** that generates **ud2** but further provides **__builtin_debugtrap()** that generates **int3** on i386 / x86-64 ([original LLVM intrinsic](http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20120507/142621.html), [further fixes](https://reviews.llvm.org/rL166300#96cef7d3), [Clang builtin support](https://reviews.llvm.org/rL166298)). + +On ARM, **debug_break()** generates **.inst 0xe7f001f0** in ARM mode and **.inst 0xde01** in Thumb mode which correctly triggers **SIGTRAP** on Linux. Unfortunately, stepping in GDB after a **debug_break()** hit doesn't work and requires a workaround like: +``` +(gdb) set $l = 2 +(gdb) tbreak *($pc + $l) +(gdb) jump *($pc + $l) +(gdb) # Change $l from 2 to 4 for ARM mode +``` +to jump over the instruction. +A new GDB command, **debugbreak-step**, is defined in [debugbreak-gdb.py](https://github.com/scottt/debugbreak/blob/master/debugbreak-gdb.py) to automate the above. See [HOW-TO-USE-DEBUGBREAK-GDB-PY.md](HOW-TO-USE-DEBUGBREAK-GDB-PY.md) for sample usage. +``` +$ arm-none-linux-gnueabi-gdb -x debugbreak-gdb.py test/break-c++ +<...> +(gdb) run +Program received signal SIGTRAP, Trace/breakpoint trap. +main () at test/break-c++.cc:6 +6 debug_break(); + +(gdb) debugbreak-step + +7 std::cout << "hello, world\n"; +``` + +On AArch64, **debug_break()** generates **.inst 0xd4200000**. + +See table below for the behavior of **debug_break()** on other architecturs. + +Behavior on Different Architectures +---------------- + +| Architecture | debug_break() | +| ------------- | ------------- | +| x86/x86-64 | `int3` | +| ARM mode, 32-bit | `.inst 0xe7f001f0` | +| Thumb mode, 32-bit | `.inst 0xde01` | +| AArch64, ARMv8 | `.inst 0xd4200000` | +| POWER | `.4byte 0x7d821008` | +| RISC-V | `.4byte 0x00100073` | +| MSVC compiler | `__debugbreak` | +| Apple compiler on AArch64 | `__builtin_trap()` | +| Otherwise | `raise(SIGTRAP)` | + diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/debugbreak-gdb.py b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/debugbreak-gdb.py new file mode 100644 index 000000000..925f61a44 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/debugbreak-gdb.py @@ -0,0 +1,183 @@ +# Copyright (c) 2013, Scott Tsai +# +# 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. +# +# 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. + + +# Usage: gdb -x debugbreak-gdb.py +# (gdb) debugbreak-step +# (gdb) debugbreak-continue +# +# To debug: +# (gdb) set python print-stack full + +import gdb +import re + +def _gdb_show_version_parse(version_str): + ''' + >>> s0 = 'This GDB was configured as "x86_64-redhat-linux-gnu".' + >>> s1 = 'This GDB was configured as "--host=i686-build_pc-linux-gnu --target=arm-linux-gnueabihf".' + >>> s2 = 'This GDB was configured as "x86_64-unknown-linux-gnu".' + >>> _gdb_show_version_parse(s0) == dict(target='x86_64-redhat-linux-gnu') + True + >>> _gdb_show_version_parse(s1) == dict(host='i686-build_pc-linux-gnu', target='arm-linux-gnueabihf') + True + >>> _gdb_show_version_parse(s2) == dict(target='x86_64-unknown-linux-gnu') + True + ''' + + t = version_str + msg = 'This GDB was configured as "' + s = t.find(msg) + if s == -1: + raise ValueError + s += len(msg) + e = t.find('".', s) + if e == -1: + raise ValueError + + config = t[s:e] + d = {} + for i in config.split(): + i = i.strip() + if i.startswith('--'): + (k, v) = i[2:].split('=') + d[k] = v + else: + if not i: + continue + d['target'] = i + return d + +def _target_triplet(): + ''' + -> 'arm-linux-gnueabihf' or 'x86_64-redhat-linux-gnu' or ... + + >>> import re + >>> not not re.match(r'\w*-\w*-\w*', target_triplet()) + True + ''' + t = gdb.execute('show version', to_string=True) + return _gdb_show_version_parse(t)['target'] + +temp_breakpoint_num = None + +def on_stop_event(e): + global temp_breakpoint_num + if not isinstance(e, gdb.BreakpointEvent): + return + for bp in e.breakpoints: + if bp.number == temp_breakpoint_num: + bp.delete() + gdb.events.stop.disconnect(on_stop_event) + l = gdb.find_pc_line(int(gdb.parse_and_eval('$pc'))).line + gdb.execute('list %d, %d' % (l, l)) + break + +def _next_instn_jump_len(gdb_frame): + '-> None means don\'t jump' + try: + arch_name = gdb_frame.architecture().name() + except AttributeError: + arch_name = None + + if arch_name.startswith('powerpc:'): + # 'powerpc:common64' on ppc64 big endian + i = bytes(gdb.selected_inferior().read_memory(gdb.parse_and_eval('$pc'), 4)) + if (i == b'\x7d\x82\x10\x08') or (i == b'\x08\x10\x82\x7d'): + return 4 + else: # not stopped on a breakpoint instruction + return None + + triplet = _target_triplet() + if re.match(r'^arm-', triplet): + i = bytes(gdb.selected_inferior().read_memory(gdb.parse_and_eval('$pc'), 4)) + if i == b'\xf0\x01\xf0\xe7': + return 4 + elif i.startswith(b'\x01\xde'): + return 2 + elif i == b'\xf0\xf7\x00\xa0 ': + # 'arm_linux_thumb2_le_breakpoint' from arm-linux-tdep.c in GDB + return 4 + else: # not stopped on a breakpoint instruction + return None + return None + +def _debugbreak_step(): + global temp_breakpoint_num + try: + frame = gdb.selected_frame() + except gdb.error as e: + # 'No frame is currently selected.' + gdb.write(e.args[0] + '\n', gdb.STDERR) + return + instn_len = _next_instn_jump_len(frame) + + if instn_len is None: + gdb.execute('stepi') + else: + loc = '*($pc + %d)' % (instn_len,) + bp = gdb.Breakpoint(loc, gdb.BP_BREAKPOINT, internal=True) + bp.silent = True + temp_breakpoint_num = bp.number + gdb.events.stop.connect(on_stop_event) + gdb.execute('jump ' + loc) + +def _debugbreak_continue(): + try: + frame = gdb.selected_frame() + except gdb.error as e: + # 'No frame is currently selected.' + gdb.write(e.args[0] + '\n', gdb.STDERR) + return + instn_len = _next_instn_jump_len(frame) + + if instn_len is None: + gdb.execute('continue') + else: + loc = '*($pc + %d)' % (instn_len,) + gdb.execute('jump ' + loc) + +class _DebugBreakStep(gdb.Command): + '''Usage: debugbreak-step + Step one instruction after a debug_break() breakpoint hit''' + + def __init__(self): + gdb.Command.__init__(self, 'debugbreak-step', gdb.COMMAND_BREAKPOINTS, gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + _debugbreak_step() + +class _DebugBreakContinue(gdb.Command): + '''Usage: debugbreak-continue + Continue execution after a debug_break() breakpoint hit''' + + def __init__(self): + gdb.Command.__init__(self, 'debugbreak-continue', gdb.COMMAND_BREAKPOINTS, gdb.COMPLETE_NONE) + + def invoke(self, arg, from_tty): + _debugbreak_continue() + +_DebugBreakStep() +_DebugBreakContinue() diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/debugbreak.h b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/debugbreak.h new file mode 100644 index 000000000..bfb828846 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/debugbreak.h @@ -0,0 +1,174 @@ +/* Copyright (c) 2011-2021, Scott Tsai + * + * 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. + * + * 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. + */ +#ifndef DEBUG_BREAK_H +#define DEBUG_BREAK_H + +#ifdef _MSC_VER + +#define debug_break __debugbreak + +#else + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEBUG_BREAK_USE_TRAP_INSTRUCTION 1 +#define DEBUG_BREAK_USE_BULTIN_TRAP 2 +#define DEBUG_BREAK_USE_SIGTRAP 3 + +#if defined(__i386__) || defined(__x86_64__) + #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION +__inline__ static void trap_instruction(void) +{ + __asm__ volatile("int $0x03"); +} +#elif defined(__thumb__) + #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION +/* FIXME: handle __THUMB_INTERWORK__ */ +__attribute__((always_inline)) +__inline__ static void trap_instruction(void) +{ + /* See 'arm-linux-tdep.c' in GDB source. + * Both instruction sequences below work. */ +#if 1 + /* 'eabi_linux_thumb_le_breakpoint' */ + __asm__ volatile(".inst 0xde01"); +#else + /* 'eabi_linux_thumb2_le_breakpoint' */ + __asm__ volatile(".inst.w 0xf7f0a000"); +#endif + + /* Known problem: + * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB. + * 'step' would keep getting stuck on the same instruction. + * + * Workaround: use the new GDB commands 'debugbreak-step' and + * 'debugbreak-continue' that become available + * after you source the script from GDB: + * + * $ gdb -x debugbreak-gdb.py <... USUAL ARGUMENTS ...> + * + * 'debugbreak-step' would jump over the breakpoint instruction with + * roughly equivalent of: + * (gdb) set $instruction_len = 2 + * (gdb) tbreak *($pc + $instruction_len) + * (gdb) jump *($pc + $instruction_len) + */ +} +#elif defined(__arm__) && !defined(__thumb__) + #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION +__attribute__((always_inline)) +__inline__ static void trap_instruction(void) +{ + /* See 'arm-linux-tdep.c' in GDB source, + * 'eabi_linux_arm_le_breakpoint' */ + __asm__ volatile(".inst 0xe7f001f0"); + /* Known problem: + * Same problem and workaround as Thumb mode */ +} +#elif defined(__aarch64__) && defined(__APPLE__) + #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BULTIN_DEBUGTRAP +#elif defined(__aarch64__) + #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION +__attribute__((always_inline)) +__inline__ static void trap_instruction(void) +{ + /* See 'aarch64-tdep.c' in GDB source, + * 'aarch64_default_breakpoint' */ + __asm__ volatile(".inst 0xd4200000"); +} +#elif defined(__powerpc__) + /* PPC 32 or 64-bit, big or little endian */ + #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION +__attribute__((always_inline)) +__inline__ static void trap_instruction(void) +{ + /* See 'rs6000-tdep.c' in GDB source, + * 'rs6000_breakpoint' */ + __asm__ volatile(".4byte 0x7d821008"); + + /* Known problem: + * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB. + * 'step' stuck on the same instruction ("twge r2,r2"). + * + * The workaround is the same as ARM Thumb mode: use debugbreak-gdb.py + * or manually jump over the instruction. */ +} +#elif defined(__riscv) + /* RISC-V 32 or 64-bit, whether the "C" extension + * for compressed, 16-bit instructions are supported or not */ + #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION +__attribute__((always_inline)) +__inline__ static void trap_instruction(void) +{ + /* See 'riscv-tdep.c' in GDB source, + * 'riscv_sw_breakpoint_from_kind' */ + __asm__ volatile(".4byte 0x00100073"); +} +#else + #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_SIGTRAP +#endif + + +#ifndef DEBUG_BREAK_IMPL +#error "debugbreak.h is not supported on this target" +#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_TRAP_INSTRUCTION +__attribute__((always_inline)) +__inline__ static void debug_break(void) +{ + trap_instruction(); +} +#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_DEBUGTRAP +__attribute__((always_inline)) +__inline__ static void debug_break(void) +{ + __builtin_debugtrap(); +} +#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_TRAP +__attribute__((always_inline)) +__inline__ static void debug_break(void) +{ + __builtin_trap(); +} +#elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_SIGTRAP +#include <signal.h> +__attribute__((always_inline)) +__inline__ static void debug_break(void) +{ + raise(SIGTRAP); +} +#else +#error "invalid DEBUG_BREAK_IMPL value" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ifdef _MSC_VER */ + +#endif /* ifndef DEBUG_BREAK_H */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break-c++.cc b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break-c++.cc new file mode 100644 index 000000000..0fcce848e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break-c++.cc @@ -0,0 +1,9 @@ +#include <iostream> +#include "debugbreak.h" + +int main() +{ + debug_break(); + std::cout << "hello, world\n"; + return 0; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break.c b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break.c new file mode 100644 index 000000000..fde701e56 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break.c @@ -0,0 +1,9 @@ +#include <stdio.h> +#include "debugbreak.h" + +int main() +{ + debug_break(); + printf("hello world\n"); + return 0; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break.gdb b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break.gdb new file mode 100644 index 000000000..e8edab33a --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break.gdb @@ -0,0 +1,2 @@ +file break +run diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/fib.c b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/fib.c new file mode 100644 index 000000000..0bd26bc22 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/fib.c @@ -0,0 +1,21 @@ +#include <stdio.h> + +#include "debugbreak.h" + +int fib(int n) +{ + int r; + if (n == 0 || n == 1) + return 1; + r = fib(n-1) + fib(n-2); + if (r == 89) { + debug_break(); + } + return r; +} + +int main() +{ + printf("%d\n", fib(15)); + return 0; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/fib.gdb b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/fib.gdb new file mode 100644 index 000000000..2cd54a4cb --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/fib.gdb @@ -0,0 +1,2 @@ +file fib +run diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/test-debugbreak.gdb b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/test-debugbreak.gdb new file mode 100644 index 000000000..96e48484e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/test-debugbreak.gdb @@ -0,0 +1,6 @@ +source ../debugbreak-gdb.py + +file break-c++ +# set remote exec-file break-c++ +run +#debugbreak-step diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/trap.c b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/trap.c new file mode 100644 index 000000000..9def30b68 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/trap.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +int main() +{ + __builtin_trap(); + printf("hello world\n"); + return 0; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/trap.gdb b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/trap.gdb new file mode 100644 index 000000000..6cd59bd98 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/trap.gdb @@ -0,0 +1,3 @@ +file trap +handle SIGILL stop nopass +run diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float.hpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float.hpp new file mode 100644 index 000000000..9e75b5e14 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float.hpp @@ -0,0 +1,28 @@ +#ifndef _C4_EXT_FAST_FLOAT_HPP_ +#define _C4_EXT_FAST_FLOAT_HPP_ + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe +#elif defined(__clang__) || defined(__APPLE_CC__) || defined(_LIBCPP_VERSION) +# pragma clang diagnostic push +# if (defined(__clang_major__) && _clang_major__ >= 9) || defined(__APPLE_CC__) +# pragma clang diagnostic ignored "-Wfortify-source" +# endif +# pragma clang diagnostic ignored "-Wshift-count-overflow" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif + +#include "c4/ext/fast_float_all.h" + +#ifdef _MSC_VER +# pragma warning(pop) +#elif defined(__clang__) || defined(__APPLE_CC__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#endif // _C4_EXT_FAST_FLOAT_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.cirrus.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.cirrus.yml new file mode 100644 index 000000000..ad6c3458b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.cirrus.yml @@ -0,0 +1,22 @@ + +task: + timeout_in: 120m + freebsd_instance: + matrix: + - image_family: freebsd-13-0-snap + + env: + ASSUME_ALWAYS_YES: YES + setup_script: + - pkg update -f + - pkg install bash + - pkg install cmake + - pkg install git + build_script: + - mkdir build + - cd build + - cmake -DFASTFLOAT_TEST=ON .. + - make + test_script: + - cd build + - ctest --output-on-failure -R basictest diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/alpine.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/alpine.yml new file mode 100644 index 000000000..825937673 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/alpine.yml @@ -0,0 +1,27 @@ +name: Alpine Linux +'on': + - push + - pull_request +jobs: + ubuntu-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: start docker + run: | + docker run -w /src -dit --name alpine -v $PWD:/src alpine:latest + echo 'docker exec alpine "$@";' > ./alpine.sh + chmod +x ./alpine.sh + - name: install packages + run: | + ./alpine.sh apk update + ./alpine.sh apk add build-base cmake g++ linux-headers git bash + - name: cmake + run: | + ./alpine.sh cmake -DFASTFLOAT_TEST=ON -B build_for_alpine + - name: build + run: | + ./alpine.sh cmake --build build_for_alpine + - name: test + run: | + ./alpine.sh bash -c "cd build_for_alpine && ctest -R basictest"
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/amalgamate-ubuntu20.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/amalgamate-ubuntu20.yml new file mode 100644 index 000000000..3d4771431 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/amalgamate-ubuntu20.yml @@ -0,0 +1,25 @@ +name: Amalgamate Ubuntu 20.04 CI (GCC 9, 8) + +on: [push, pull_request] + +jobs: + ubuntu-build: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + include: + # Legacy/x86 compilers cause CI failures. + #- {cxx: -DCMAKE_CXX_COMPILER=g++-8, arch: } + - {cxx: , arch: } # default=gcc9 + #- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9 + steps: + - uses: actions/checkout@v2 + - name: Compile with amalgamation + run: | + mkdir build && + mkdir build/fast_float && + python3 ./script/amalgamate.py > build/fast_float/fast_float.h && + cp tests/string_test.cpp build/ && + cd build && + g++ string_test.cpp diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/msys2-clang.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/msys2-clang.yml new file mode 100644 index 000000000..ba16f4367 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/msys2-clang.yml @@ -0,0 +1,39 @@ +name: MSYS2-CLANG-CI + +on: [push, pull_request] + +jobs: + windows-mingw: + name: ${{ matrix.msystem }} + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + strategy: + fail-fast: false + matrix: + include: + - msystem: "MINGW64" + install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang + type: Release + - msystem: "MINGW32" + install: mingw-w64-i686-libxml2 mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang + type: Release + env: + CMAKE_GENERATOR: Ninja + + steps: + - uses: actions/checkout@v2 + - uses: msys2/setup-msys2@v2 + with: + update: true + msystem: ${{ matrix.msystem }} + install: ${{ matrix.install }} + - name: Prepare build dir + run: mkdir build + - name: Configure + run: cd build && cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build + - name: Run basic tests + run: cd build && ctest --output-on-failure -R basictest diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/msys2.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/msys2.yml new file mode 100644 index 000000000..665c53916 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/msys2.yml @@ -0,0 +1,45 @@ +name: MSYS2-CI + +on: [push, pull_request] + +jobs: + windows-mingw: + name: ${{ matrix.msystem }} + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + strategy: + fail-fast: false + matrix: + include: + - msystem: "MINGW64" + install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc + type: Release + - msystem: "MINGW32" + install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc + type: Release + - msystem: "MINGW64" + install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc + type: Debug + - msystem: "MINGW32" + install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc + type: Debug + env: + CMAKE_GENERATOR: Ninja + + steps: + - uses: actions/checkout@v2 + - uses: msys2/setup-msys2@v2 + with: + update: true + msystem: ${{ matrix.msystem }} + install: ${{ matrix.install }} + - name: Prepare build dir + run: mkdir build + - name: Configure + run: cd build && cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build + - name: Run basic tests + run: cd build && ctest --output-on-failure -R basictest diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu18.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu18.yml new file mode 100644 index 000000000..dbdaa7adc --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu18.yml @@ -0,0 +1,35 @@ +name: Ubuntu 18.04 CI (GCC 7, 6, 5) + +on: [push, pull_request] + +jobs: + ubuntu-build: + runs-on: ubuntu-18.04 + strategy: + fail-fast: false + matrix: + include: + # Legacy/x86 compilers cause CI failures. + #- {cxx: -DCMAKE_CXX_COMPILER=g++-5, arch: } + #- {cxx: -DCMAKE_CXX_COMPILER=g++-6, arch: } + - {cxx: , arch: } # default=gcc7 + #- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc7 + steps: + - uses: actions/checkout@v2 + - name: Setup cmake + uses: jwlawson/[email protected] + with: + cmake-version: '3.11.x' + #- name: Install older compilers + # run: | + # sudo -E dpkg --add-architecture i386 + # sudo -E apt-get update + # sudo -E apt-get install -y --force-yes g++-5 g++-6 g++-5-multilib g++-6-multilib g++-multilib linux-libc-dev:i386 libc6:i386 libc6-dev:i386 libc6-dbg:i386 + - name: Prepare build dir + run: mkdir build + - name: Configure + run: cd build && cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build + - name: Run basic tests + run: cd build && ctest --output-on-failure -R basictest diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu20-cxx20.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu20-cxx20.yml new file mode 100644 index 000000000..a8b9acbc2 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu20-cxx20.yml @@ -0,0 +1,19 @@ +name: Ubuntu 20.04 CI (C++20) + +on: [push, pull_request] + +jobs: + ubuntu-build: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + steps: + - uses: actions/checkout@v2 + - name: Use cmake + run: | + mkdir build && + cd build && + cmake -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. && + cmake --build . && + ctest --output-on-failure && + cmake --install . diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu20.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu20.yml new file mode 100644 index 000000000..ebd8b23bc --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu20.yml @@ -0,0 +1,29 @@ +name: Ubuntu 20.04 CI (GCC 9) + +on: [push, pull_request] + +jobs: + ubuntu-build: + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + include: + # Legacy/x86 compilers cause CI failures. + #- {cxx: -DCMAKE_CXX_COMPILER=g++-8, arch: } + - {cxx: , arch: } # default=gcc9 + #- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9 + steps: + - uses: actions/checkout@v2 + - name: Use cmake + run: | + mkdir build && + cd build && + cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. && + cmake --build . && + ctest --output-on-failure && + cmake --install . && + cd ../tests/installation_tests/find && + mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . && + cd ../../issue72_installation && + mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs15-ci.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs15-ci.yml new file mode 100644 index 000000000..a6207a7ad --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs15-ci.yml @@ -0,0 +1,29 @@ +name: VS15-CI + +on: [push, pull_request] + +jobs: + ci: + if: >- + ! contains(toJSON(github.event.commits.*.message), '[skip ci]') && + ! contains(toJSON(github.event.commits.*.message), '[skip github]') + name: windows-vs15 + runs-on: windows-2016 + strategy: + fail-fast: false + matrix: + include: + - {gen: Visual Studio 15 2017, arch: Win32} + - {gen: Visual Studio 15 2017, arch: x64} + steps: + - uses: actions/checkout@v2 + - name: Configure + run: | + mkdir build + cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build --verbose --config Release --parallel + - name: 'Run CTest' + run: | + cd build + ctest -C Release --output-on-failure -R basictest
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-arm-ci.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-arm-ci.yml new file mode 100644 index 000000000..7c69e38f1 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-arm-ci.yml @@ -0,0 +1,21 @@ +name: VS16-ARM-CI + +on: [push, pull_request] + +jobs: + ci: + name: windows-vs16 + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - {arch: ARM} + - {arch: ARM64} + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Use cmake + run: | + cmake -A ${{ matrix.arch }} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON -B build && + cmake --build build --verbose
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-ci.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-ci.yml new file mode 100644 index 000000000..fe40dddc9 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-ci.yml @@ -0,0 +1,29 @@ +name: VS16-CI + +on: [push, pull_request] + +jobs: + ci: + name: windows-vs16 + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - {gen: Visual Studio 16 2019, arch: Win32} + - {gen: Visual Studio 16 2019, arch: x64} + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Use cmake + run: | + mkdir build && + cd build && + cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. && + cmake --build . --verbose && + ctest --output-on-failure && + cmake --install . && + cd ../tests/installation_tests/find && + mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . --verbose + cd ../../issue72_installation && + mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . --verbose
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-clang-ci.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-clang-ci.yml new file mode 100644 index 000000000..a32c6b822 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-clang-ci.yml @@ -0,0 +1,27 @@ +name: VS16-CLANG-CI + +on: [push, pull_request] + +jobs: + ci: + name: windows-vs16 + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - {gen: Visual Studio 16 2019, arch: Win32} + - {gen: Visual Studio 16 2019, arch: x64} + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Configure + run: | + mkdir build + cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON .. + - name: Build + run: cmake --build build --config Release --parallel + - name: Run basic tests + run: | + cd build + ctest -C Release --output-on-failure -R basictest diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-cxx20.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-cxx20.yml new file mode 100644 index 000000000..3a770935f --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-cxx20.yml @@ -0,0 +1,24 @@ +name: VS16-CI C++20 + +on: [push, pull_request] + +jobs: + ci: + name: windows-vs16 + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - {gen: Visual Studio 16 2019, arch: Win32} + - {gen: Visual Studio 16 2019, arch: x64} + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Use cmake + run: | + mkdir build && + cd build && + cmake ${{matrix.cxx}} ${{matrix.arch}} -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. && + cmake --build . --verbose && + ctest --output-on-failure diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.gitignore b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.gitignore new file mode 100644 index 000000000..15665575e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.gitignore @@ -0,0 +1,4 @@ +build/* +Testing/* +.cache/ +compile_commands.json diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.travis.yml b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.travis.yml new file mode 100644 index 000000000..ae854aed8 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.travis.yml @@ -0,0 +1,242 @@ +language: cpp + +dist: bionic + +cache: + directories: + - $HOME/.dep_cache + +env: + global: + - fastfloat_DEPENDENCY_CACHE_DIR=$HOME/.dep_cache + +services: + - docker + +# the ppc64le and s390x images use cmake 3.10, but fast_float requires 3.11. +# so we compile cmake from source in those images. +# - tried the kitware ppa but that is using 3.10 as well +# - tried also using snap to get a more recent version but that failed with +# udev errors. + +matrix: + include: + - arch: ppc64le + os: linux + env: + - CMAKE_SRC="https://github.com/Kitware/CMake/releases/download/v3.11.4/cmake-3.11.4.tar.gz" + + - arch: s390x + os: linux + env: + - CMAKE_SRC="https://github.com/Kitware/CMake/releases/download/v3.11.4/cmake-3.11.4.tar.gz" + + - arch: amd64 + os: linux + + - arch: amd64 + os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 + env: + - COMPILER="CC=gcc-8 && CXX=g++-8" + compiler: gcc-8 + + - arch: amd64 + os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-9 + env: + - COMPILER="CC=gcc-9 && CXX=g++-9" + compiler: gcc-9 + + - arch: amd64 + os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-10 + env: + - COMPILER="CC=gcc-10 && CXX=g++-10" + compiler: gcc-10 + + - arch: amd64 + os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-10 + env: + - COMPILER="CC=gcc-10 && CXX=g++-10" + - SANITIZE="on" + compiler: gcc-10-sanitize + + - arch: amd64 + os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-10 + env: + - COMPILER="CC=gcc-10 && CXX=g++-10" + - STATIC="on" + acompiler: gcc-10-static + + - arch: amd64 + os: linux + addons: + apt: + sources: + - llvm-toolchain-bionic-6.0 + packages: + - clang-6.0 + env: + - COMPILER="CC=clang-6.0 && CXX=clang++-6.0" + compiler: clang-6 + + - arch: amd64 + os: linux + addons: + apt: + sources: + - llvm-toolchain-bionic-7 + packages: + - clang-7 + env: + - COMPILER="CC=clang-7 && CXX=clang++-7" + compiler: clang-7 + + - arch: amd64 + os: linux + addons: + apt: + sources: + - llvm-toolchain-bionic-8 + packages: + - clang-8 + env: + - COMPILER="CC=clang-8 && CXX=clang++-8" + compiler: clang-8 + + - arch: amd64 + os: linux + addons: + apt: + sources: + - llvm-toolchain-bionic-9 + packages: + - clang-9 + env: + - COMPILER="CC=clang-9 && CXX=clang++-9" + compiler: clang-9 + + - arch: amd64 + os: linux + addons: + apt: + packages: + - clang-10 + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + env: + - COMPILER="CC=clang-10 && CXX=clang++-10" + compiler: clang-10 + + - arch: amd64 + os: linux + addons: + apt: + packages: + - clang-10 + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + env: + - COMPILER="CC=clang-10 && CXX=clang++-10" + - STATIC="on" + compiler: clang-10-static + + - arch: amd64 + os: linux + addons: + apt: + packages: + - clang-10 + sources: + - ubuntu-toolchain-r-test + - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + env: + - COMPILER="CC=clang-10 && CXX=clang++-10" + - SANITIZE="on" + compiler: clang-10-sanitize + + - arch: amd64 + os: linux + env: + - TOOLCHAIN="mips64" + + - arch: amd64 + os: linux + env: + - TOOLCHAIN="riscv64" + +before_install: + - eval "${COMPILER}" + - | + if [ "$TOOLCHAIN" != "" ] ; then + docker pull ahuszagh/cross:"$TOOLCHAIN" + fi + +install: + - | + if [ "$CMAKE_SRC" != "" ] ; then + set -x + set -e + sudo -E apt remove --purge cmake + sudo -E apt-get update + sudo -E apt-get install -y build-essential libssl-dev + mkdir cmake_src + pushd cmake_src + wget "$CMAKE_SRC" + tar xfz $(basename "$CMAKE_SRC") + pushd $(basename "$CMAKE_SRC" | sed "s:.tar.gz::") + ./bootstrap + make -j2 + sudo make install + popd + popd + set +x + fi + - echo ${PATH} + - which cmake + - cmake --version + - which ${CC} + - ${CC} --version + - which ${CXX} + - ${CXX} --version + +script: + - | + if [ "$TOOLCHAIN" != "" ] ; then + docker run -v "$(pwd)":/ff ahuszagh/cross:"$TOOLCHAIN" /bin/bash -c "cd ff && ci/script.sh $TOOLCHAIN" + else + ci/script.sh + fi diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/AUTHORS b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/AUTHORS new file mode 100644 index 000000000..60c942582 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/AUTHORS @@ -0,0 +1,2 @@ +Daniel Lemire +João Paulo Magalhaes diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/CONTRIBUTORS b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/CONTRIBUTORS new file mode 100644 index 000000000..58a037cc5 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/CONTRIBUTORS @@ -0,0 +1,6 @@ +Eugene Golushkov +Maksim Kita +Marcin Wojdyr +Neal Richardson +Tim Paine +Fabio Pellacini diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/LICENSE-APACHE b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/LICENSE-APACHE new file mode 100644 index 000000000..79e59131d --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 The fast_float authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/LICENSE-MIT b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/LICENSE-MIT new file mode 100644 index 000000000..31aa79387 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/LICENSE-MIT @@ -0,0 +1,23 @@ +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/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/README.md b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/README.md new file mode 100644 index 000000000..059b9dade --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/README.md @@ -0,0 +1,216 @@ +## fast_float number parsing library: 4x faster than strtod + +/badge.svg) +/badge.svg) + + + +[](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml) + +The fast_float library provides fast header-only implementations for the C++ from_chars +functions for `float` and `double` types. These functions convert ASCII strings representing +decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including +round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries. + +Specifically, `fast_float` provides the following two functions with a C++17-like syntax (the library itself only requires C++11): + +```C++ +from_chars_result from_chars(const char* first, const char* last, float& value, ...); +from_chars_result from_chars(const char* first, const char* last, double& value, ...); +``` + +The return type (`from_chars_result`) is defined as the struct: +```C++ +struct from_chars_result { + const char* ptr; + std::errc ec; +}; +``` + +It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting +a locale-independent format equivalent to the C++17 from_chars function. +The resulting floating-point value is the closest floating-point values (using either float or double), +using the "round to even" convention for values that would otherwise fall right in-between two values. +That is, we provide exact parsing according to the IEEE standard. + + +Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the +parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned +`ec` contains a representative error, otherwise the default (`std::errc()`) value is stored. + +The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`). + +It will parse infinity and nan values. + +Example: + +``` C++ +#include "fast_float/fast_float.h" +#include <iostream> + +int main() { + const std::string input = "3.1416 xyz "; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); + if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } + std::cout << "parsed the number " << result << std::endl; + return EXIT_SUCCESS; +} +``` + + +Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of +the type `fast_float::chars_format`. It is a bitset value: we check whether +`fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set +to determine whether we allow the fixed point and scientific notation respectively. +The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. + +The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification. +* The `from_chars` function does not skip leading white-space characters. +* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden. +* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers. + +Furthermore, we have the following restrictions: +* We only support `float` and `double` types at this time. +* We only support the decimal format: we do not support hexadecimal strings. +* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value. + +We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems. + + + +## Using commas as decimal separator + + +The C++ standard stipulate that `from_chars` has to be locale-independent. In +particular, the decimal separator has to be the period (`.`). However, +some users still want to use the `fast_float` library with in a locale-dependent +manner. Using a separate function called `from_chars_advanced`, we allow the users +to pass a `parse_options` instance which contains a custom decimal separator (e.g., +the comma). You may use it as follows. + +```C++ +#include "fast_float/fast_float.h" +#include <iostream> + +int main() { + const std::string input = "3,1416 xyz "; + double result; + fast_float::parse_options options{fast_float::chars_format::general, ','}; + auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options); + if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } + std::cout << "parsed the number " << result << std::endl; + return EXIT_SUCCESS; +} +``` + + +## Reference + +- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021. + +## Other programming languages + +- [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`. +- [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`. +- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. +- [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`. + + +## Relation With Other Work + +The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM). + +## Users + +The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet). + + +## How fast is it? + +It can parse random floating-point numbers at a speed of 1 GB/s on some systems. We find that it is often twice as fast as the best available competitor, and many times faster than many standard-library implementations. + +<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400"> + +``` +$ ./build/benchmarks/benchmark +# parsing random integers in the range [0,1) +volume = 2.09808 MB +netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s +doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s +strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s +abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s +fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s +``` + +See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code. + + +## Video + +[](http://www.youtube.com/watch?v=AVXgvlMeIm4)<br /> + +## Using as a CMake dependency + +This library is header-only by design. The CMake file provides the `fast_float` target +which is merely a pointer to the `include` directory. + +If you drop the `fast_float` repository in your CMake project, you should be able to use +it in this manner: + +```cmake +add_subdirectory(fast_float) +target_link_libraries(myprogram PUBLIC fast_float) +``` + +Or you may want to retrieve the dependency automatically if you have a sufficiently recent version of CMake (3.11 or better at least): + +```cmake +FetchContent_Declare( + fast_float + GIT_REPOSITORY https://github.com/lemire/fast_float.git + GIT_TAG tags/v1.1.2 + GIT_SHALLOW TRUE) + +FetchContent_MakeAvailable(fast_float) +target_link_libraries(myprogram PUBLIC fast_float) + +``` + +You should change the `GIT_TAG` line so that you recover the version you wish to use. + +## Using as single header + +The script `script/amalgamate.py` may be used to generate a single header +version of the library if so desired. +Just run the script from the root directory of this repository. +You can customize the license type and output file if desired as described in +the command line help. + +You may directly download automatically generated single-header files: + +https://github.com/fastfloat/fast_float/releases/download/v1.1.2/fast_float.h + +## Credit + +Though this work is inspired by many different people, this work benefited especially from exchanges with +Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided +invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits. + +The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published +under the Apache 2.0 license. + +## License + +<sup> +Licensed under either of <a href="LICENSE-APACHE">Apache License, Version +2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option. +</sup> + +<br> + +<sub> +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in this repository by you, as defined in the Apache-2.0 license, +shall be dual licensed as above, without any additional terms or conditions. +</sub> diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/ci/script.sh b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/ci/script.sh new file mode 100755 index 000000000..c9894c7a2 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/ci/script.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +TOOLCHAIN="$1" + +mkdir build +cd build + +if [ "$TOOLCHAIN" != "" ] ; then + cmake -DFASTFLOAT_TEST=ON .. -DCMAKE_TOOLCHAIN_FILE=/toolchains/"$TOOLCHAIN".cmake +else + cmake -DFASTFLOAT_TEST=ON .. +fi +make -j 2 +if [ "$TOOLCHAIN" != "" ] ; then + qemu-"$TOOLCHAIN" tests/basictest +else + ctest --output-on-failure -R basictest +fi diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/cmake/config.cmake.in b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/cmake/config.cmake.in new file mode 100644 index 000000000..035dc0fa0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/cmake/config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]") +check_required_components("@PROJECT_NAME@") diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/ascii_number.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/ascii_number.h new file mode 100644 index 000000000..3e6bb3e9e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/ascii_number.h @@ -0,0 +1,231 @@ +#ifndef FASTFLOAT_ASCII_NUMBER_H +#define FASTFLOAT_ASCII_NUMBER_H + +#include <cctype> +#include <cstdint> +#include <cstring> +#include <iterator> + +#include "float_common.h" + +namespace fast_float { + +// Next function can be micro-optimized, but compilers are entirely +// able to optimize it well. +fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } + +fastfloat_really_inline uint64_t byteswap(uint64_t val) { + return (val & 0xFF00000000000000) >> 56 + | (val & 0x00FF000000000000) >> 40 + | (val & 0x0000FF0000000000) >> 24 + | (val & 0x000000FF00000000) >> 8 + | (val & 0x00000000FF000000) << 8 + | (val & 0x0000000000FF0000) << 24 + | (val & 0x000000000000FF00) << 40 + | (val & 0x00000000000000FF) << 56; +} + +fastfloat_really_inline uint64_t read_u64(const char *chars) { + uint64_t val; + ::memcpy(&val, chars, sizeof(uint64_t)); +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + return val; +} + +fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + ::memcpy(chars, &val, sizeof(uint64_t)); +} + +// credit @aqrit +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { + const uint64_t mask = 0x000000FF000000FF; + const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return uint32_t(val); +} + +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { + return parse_eight_digits_unrolled(read_u64(chars)); +} + +// credit @aqrit +fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { + return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & + 0x8080808080808080)); +} + +fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { + return is_made_of_eight_digits_fast(read_u64(chars)); +} + +typedef span<const char> byte_span; + +struct parsed_number_string { + int64_t exponent{0}; + uint64_t mantissa{0}; + const char *lastmatch{nullptr}; + bool negative{false}; + bool valid{false}; + bool too_many_digits{false}; + // contains the range of the significant digits + byte_span integer{}; // non-nullable + byte_span fraction{}; // nullable +}; + +// Assuming that you use no more than 19 digits, this will +// parse an ASCII string. +fastfloat_really_inline +parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { + const chars_format fmt = options.format; + const char decimal_point = options.decimal_point; + + parsed_number_string answer; + answer.valid = false; + answer.too_many_digits = false; + answer.negative = (*p == '-'); + if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + ++p; + if (p == pend) { + return answer; + } + if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot + return answer; + } + } + const char *const start_digits = p; + + uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) + + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + + uint64_t(*p - '0'); // might overflow, we will handle the overflow later + ++p; + } + const char *const end_of_integer_part = p; + int64_t digit_count = int64_t(end_of_integer_part - start_digits); + answer.integer = byte_span(start_digits, size_t(digit_count)); + int64_t exponent = 0; + if ((p != pend) && (*p == decimal_point)) { + ++p; + const char* before = p; + // can occur at most twice without overflowing, but let it occur more, since + // for integers with many digits, digit parsing is the primary bottleneck. + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + ++p; + i = i * 10 + digit; // in rare cases, this will overflow, but that's ok + } + exponent = before - p; + answer.fraction = byte_span(before, size_t(p - before)); + digit_count -= exponent; + } + // we must have encountered at least one integer! + if (digit_count == 0) { + return answer; + } + int64_t exp_number = 0; // explicit exponential part + if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { + const char * location_of_e = p; + ++p; + bool neg_exp = false; + if ((p != pend) && ('-' == *p)) { + neg_exp = true; + ++p; + } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) + ++p; + } + if ((p == pend) || !is_integer(*p)) { + if(!(fmt & chars_format::fixed)) { + // We are in error. + return answer; + } + // Otherwise, we will be ignoring the 'e'. + p = location_of_e; + } else { + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + if (exp_number < 0x10000000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + if(neg_exp) { exp_number = - exp_number; } + exponent += exp_number; + } + } else { + // If it scientific and not fixed, we have to bail out. + if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; } + } + answer.lastmatch = p; + answer.valid = true; + + // If we frequently had to deal with long strings of digits, + // we could extend our code by using a 128-bit integer instead + // of a 64-bit integer. However, this is uncommon. + // + // We can deal with up to 19 digits. + if (digit_count > 19) { // this is uncommon + // It is possible that the integer had an overflow. + // We have to handle the case where we have 0.0000somenumber. + // We need to be mindful of the case where we only have zeroes... + // E.g., 0.000000000...000. + const char *start = start_digits; + while ((start != pend) && (*start == '0' || *start == decimal_point)) { + if(*start == '0') { digit_count --; } + start++; + } + if (digit_count > 19) { + answer.too_many_digits = true; + // Let us start again, this time, avoiding overflows. + // We don't need to check if is_integer, since we use the + // pre-tokenized spans from above. + i = 0; + p = answer.integer.ptr; + const char* int_end = p + answer.integer.len(); + const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; + while((i < minimal_nineteen_digit_integer) && (p != int_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + if (i >= minimal_nineteen_digit_integer) { // We have a big integers + exponent = end_of_integer_part - p + exp_number; + } else { // We have a value with a fractional component. + p = answer.fraction.ptr; + const char* frac_end = p + answer.fraction.len(); + while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + exponent = answer.fraction.ptr - p + exp_number; + } + // We have now corrected both exponent and i, to a truncated value + } + } + answer.exponent = exponent; + answer.mantissa = i; + return answer; +} + +} // namespace fast_float + +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/bigint.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/bigint.h new file mode 100644 index 000000000..b56cb9b03 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/bigint.h @@ -0,0 +1,590 @@ +#ifndef FASTFLOAT_BIGINT_H +#define FASTFLOAT_BIGINT_H + +#include <algorithm> +#include <cstdint> +#include <climits> +#include <cstring> + +#include "float_common.h" + +namespace fast_float { + +// the limb width: we want efficient multiplication of double the bits in +// limb, or for 64-bit limbs, at least 64-bit multiplication where we can +// extract the high and low parts efficiently. this is every 64-bit +// architecture except for sparc, which emulates 128-bit multiplication. +// we might have platforms where `CHAR_BIT` is not 8, so let's avoid +// doing `8 * sizeof(limb)`. +#if defined(FASTFLOAT_64BIT) && !defined(__sparc) +#define FASTFLOAT_64BIT_LIMB +typedef uint64_t limb; +constexpr size_t limb_bits = 64; +#else +#define FASTFLOAT_32BIT_LIMB +typedef uint32_t limb; +constexpr size_t limb_bits = 32; +#endif + +typedef span<limb> limb_span; + +// number of bits in a bigint. this needs to be at least the number +// of bits required to store the largest bigint, which is +// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or +// ~3600 bits, so we round to 4000. +constexpr size_t bigint_bits = 4000; +constexpr size_t bigint_limbs = bigint_bits / limb_bits; + +// vector-like type that is allocated on the stack. the entire +// buffer is pre-allocated, and only the length changes. +template <uint16_t size> +struct stackvec { + limb data[size]; + // we never need more than 150 limbs + uint16_t length{0}; + + stackvec() = default; + stackvec(const stackvec &) = delete; + stackvec &operator=(const stackvec &) = delete; + stackvec(stackvec &&) = delete; + stackvec &operator=(stackvec &&other) = delete; + + // create stack vector from existing limb span. + stackvec(limb_span s) { + FASTFLOAT_ASSERT(try_extend(s)); + } + + limb& operator[](size_t index) noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + const limb& operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + // index from the end of the container + const limb& rindex(size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + size_t rindex = length - index - 1; + return data[rindex]; + } + + // set the length, without bounds checking. + void set_len(size_t len) noexcept { + length = uint16_t(len); + } + constexpr size_t len() const noexcept { + return length; + } + constexpr bool is_empty() const noexcept { + return length == 0; + } + constexpr size_t capacity() const noexcept { + return size; + } + // append item to vector, without bounds checking + void push_unchecked(limb value) noexcept { + data[length] = value; + length++; + } + // append item to vector, returning if item was added + bool try_push(limb value) noexcept { + if (len() < capacity()) { + push_unchecked(value); + return true; + } else { + return false; + } + } + // add items to the vector, from a span, without bounds checking + void extend_unchecked(limb_span s) noexcept { + limb* ptr = data + length; + ::memcpy((void*)ptr, (const void*)s.ptr, sizeof(limb) * s.len()); + set_len(len() + s.len()); + } + // try to add items to the vector, returning if items were added + bool try_extend(limb_span s) noexcept { + if (len() + s.len() <= capacity()) { + extend_unchecked(s); + return true; + } else { + return false; + } + } + // resize the vector, without bounds checking + // if the new size is longer than the vector, assign value to each + // appended item. + void resize_unchecked(size_t new_len, limb value) noexcept { + if (new_len > len()) { + size_t count = new_len - len(); + limb* first = data + len(); + limb* last = first + count; + ::std::fill(first, last, value); + set_len(new_len); + } else { + set_len(new_len); + } + } + // try to resize the vector, returning if the vector was resized. + bool try_resize(size_t new_len, limb value) noexcept { + if (new_len > capacity()) { + return false; + } else { + resize_unchecked(new_len, value); + return true; + } + } + // check if any limbs are non-zero after the given index. + // this needs to be done in reverse order, since the index + // is relative to the most significant limbs. + bool nonzero(size_t index) const noexcept { + while (index < len()) { + if (rindex(index) != 0) { + return true; + } + index++; + } + return false; + } + // normalize the big integer, so most-significant zero limbs are removed. + void normalize() noexcept { + while (len() > 0 && rindex(0) == 0) { + length--; + } + } +}; + +fastfloat_really_inline +uint64_t empty_hi64(bool& truncated) noexcept { + truncated = false; + return 0; +} + +fastfloat_really_inline +uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept { + truncated = false; + int shl = leading_zeroes(r0); + return r0 << shl; +} + +fastfloat_really_inline +uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept { + int shl = leading_zeroes(r0); + if (shl == 0) { + truncated = r1 != 0; + return r0; + } else { + int shr = 64 - shl; + truncated = (r1 << shl) != 0; + return (r0 << shl) | (r1 >> shr); + } +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept { + return uint64_hi64(r0, truncated); +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + return uint64_hi64((x0 << 32) | x1, truncated); +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + uint64_t x2 = r2; + return uint64_hi64(x0, (x1 << 32) | x2, truncated); +} + +// add two small integers, checking for overflow. +// we want an efficient operation. for msvc, where +// we don't have built-in intrinsics, this is still +// pretty fast. +fastfloat_really_inline +limb scalar_add(limb x, limb y, bool& overflow) noexcept { + limb z; + +// gcc and clang +#if defined(__has_builtin) + #if __has_builtin(__builtin_add_overflow) + overflow = __builtin_add_overflow(x, y, &z); + return z; + #endif +#endif + + // generic, this still optimizes correctly on MSVC. + z = x + y; + overflow = z < x; + return z; +} + +// multiply two small integers, getting both the high and low bits. +fastfloat_really_inline +limb scalar_mul(limb x, limb y, limb& carry) noexcept { +#ifdef FASTFLOAT_64BIT_LIMB + #if defined(__SIZEOF_INT128__) + // GCC and clang both define it as an extension. + __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry); + carry = limb(z >> limb_bits); + return limb(z); + #else + // fallback, no native 128-bit integer multiplication with carry. + // on msvc, this optimizes identically, somehow. + value128 z = full_multiplication(x, y); + bool overflow; + z.low = scalar_add(z.low, carry, overflow); + z.high += uint64_t(overflow); // cannot overflow + carry = z.high; + return z.low; + #endif +#else + uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry); + carry = limb(z >> limb_bits); + return limb(z); +#endif +} + +// add scalar value to bigint starting from offset. +// used in grade school multiplication +template <uint16_t size> +inline bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept { + size_t index = start; + limb carry = y; + bool overflow; + while (carry != 0 && index < vec.len()) { + vec[index] = scalar_add(vec[index], carry, overflow); + carry = limb(overflow); + index += 1; + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add scalar value to bigint. +template <uint16_t size> +fastfloat_really_inline bool small_add(stackvec<size>& vec, limb y) noexcept { + return small_add_from(vec, y, 0); +} + +// multiply bigint by scalar value. +template <uint16_t size> +inline bool small_mul(stackvec<size>& vec, limb y) noexcept { + limb carry = 0; + for (size_t index = 0; index < vec.len(); index++) { + vec[index] = scalar_mul(vec[index], y, carry); + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add bigint to bigint starting from index. +// used in grade school multiplication +template <uint16_t size> +bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept { + // the effective x buffer is from `xstart..x.len()`, so exit early + // if we can't get that current range. + if (x.len() < start || y.len() > x.len() - start) { + FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); + } + + bool carry = false; + for (size_t index = 0; index < y.len(); index++) { + limb xi = x[index + start]; + limb yi = y[index]; + bool c1 = false; + bool c2 = false; + xi = scalar_add(xi, yi, c1); + if (carry) { + xi = scalar_add(xi, 1, c2); + } + x[index + start] = xi; + carry = c1 | c2; + } + + // handle overflow + if (carry) { + FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); + } + return true; +} + +// add bigint to bigint. +template <uint16_t size> +fastfloat_really_inline bool large_add_from(stackvec<size>& x, limb_span y) noexcept { + return large_add_from(x, y, 0); +} + +// grade-school multiplication algorithm +template <uint16_t size> +bool long_mul(stackvec<size>& x, limb_span y) noexcept { + limb_span xs = limb_span(x.data, x.len()); + stackvec<size> z(xs); + limb_span zs = limb_span(z.data, z.len()); + + if (y.len() != 0) { + limb y0 = y[0]; + FASTFLOAT_TRY(small_mul(x, y0)); + for (size_t index = 1; index < y.len(); index++) { + limb yi = y[index]; + stackvec<size> zi; + if (yi != 0) { + // re-use the same buffer throughout + zi.set_len(0); + FASTFLOAT_TRY(zi.try_extend(zs)); + FASTFLOAT_TRY(small_mul(zi, yi)); + limb_span zis = limb_span(zi.data, zi.len()); + FASTFLOAT_TRY(large_add_from(x, zis, index)); + } + } + } + + x.normalize(); + return true; +} + +// grade-school multiplication algorithm +template <uint16_t size> +bool large_mul(stackvec<size>& x, limb_span y) noexcept { + if (y.len() == 1) { + FASTFLOAT_TRY(small_mul(x, y[0])); + } else { + FASTFLOAT_TRY(long_mul(x, y)); + } + return true; +} + +// big integer type. implements a small subset of big integer +// arithmetic, using simple algorithms since asymptotically +// faster algorithms are slower for a small number of limbs. +// all operations assume the big-integer is normalized. +struct bigint { + // storage of the limbs, in little-endian order. + stackvec<bigint_limbs> vec; + + bigint(): vec() {} + bigint(const bigint &) = delete; + bigint &operator=(const bigint &) = delete; + bigint(bigint &&) = delete; + bigint &operator=(bigint &&other) = delete; + + bigint(uint64_t value): vec() { +#ifdef FASTFLOAT_64BIT_LIMB + vec.push_unchecked(value); +#else + vec.push_unchecked(uint32_t(value)); + vec.push_unchecked(uint32_t(value >> 32)); +#endif + vec.normalize(); + } + + // get the high 64 bits from the vector, and if bits were truncated. + // this is to get the significant digits for the float. + uint64_t hi64(bool& truncated) const noexcept { +#ifdef FASTFLOAT_64BIT_LIMB + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint64_hi64(vec.rindex(0), truncated); + } else { + uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated); + truncated |= vec.nonzero(2); + return result; + } +#else + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint32_hi64(vec.rindex(0), truncated); + } else if (vec.len() == 2) { + return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated); + } else { + uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated); + truncated |= vec.nonzero(3); + return result; + } +#endif + } + + // compare two big integers, returning the large value. + // assumes both are normalized. if the return value is + // negative, other is larger, if the return value is + // positive, this is larger, otherwise they are equal. + // the limbs are stored in little-endian order, so we + // must compare the limbs in ever order. + int compare(const bigint& other) const noexcept { + if (vec.len() > other.vec.len()) { + return 1; + } else if (vec.len() < other.vec.len()) { + return -1; + } else { + for (size_t index = vec.len(); index > 0; index--) { + limb xi = vec[index - 1]; + limb yi = other.vec[index - 1]; + if (xi > yi) { + return 1; + } else if (xi < yi) { + return -1; + } + } + return 0; + } + } + + // shift left each limb n bits, carrying over to the new limb + // returns true if we were able to shift all the digits. + bool shl_bits(size_t n) noexcept { + // Internally, for each item, we shift left by n, and add the previous + // right shifted limb-bits. + // For example, we transform (for u8) shifted left 2, to: + // b10100100 b01000010 + // b10 b10010001 b00001000 + FASTFLOAT_DEBUG_ASSERT(n != 0); + FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); + + size_t shl = n; + size_t shr = limb_bits - shl; + limb prev = 0; + for (size_t index = 0; index < vec.len(); index++) { + limb xi = vec[index]; + vec[index] = (xi << shl) | (prev >> shr); + prev = xi; + } + + limb carry = prev >> shr; + if (carry != 0) { + return vec.try_push(carry); + } + return true; + } + + // move the limbs left by `n` limbs. + bool shl_limbs(size_t n) noexcept { + FASTFLOAT_DEBUG_ASSERT(n != 0); + if (n + vec.len() > vec.capacity()) { + return false; + } else if (!vec.is_empty()) { + // move limbs + limb* dst = vec.data + n; + const limb* src = vec.data; + ::memmove(dst, src, sizeof(limb) * vec.len()); + // fill in empty limbs + limb* first = vec.data; + limb* last = first + n; + ::std::fill(first, last, 0); + vec.set_len(n + vec.len()); + return true; + } else { + return true; + } + } + + // move the limbs left by `n` bits. + bool shl(size_t n) noexcept { + size_t rem = n % limb_bits; + size_t div = n / limb_bits; + if (rem != 0) { + FASTFLOAT_TRY(shl_bits(rem)); + } + if (div != 0) { + FASTFLOAT_TRY(shl_limbs(div)); + } + return true; + } + + // get the number of leading zeros in the bigint. + int ctlz() const noexcept { + if (vec.is_empty()) { + return 0; + } else { +#ifdef FASTFLOAT_64BIT_LIMB + return leading_zeroes(vec.rindex(0)); +#else + // no use defining a specialized leading_zeroes for a 32-bit type. + uint64_t r0 = vec.rindex(0); + return leading_zeroes(r0 << 32); +#endif + } + } + + // get the number of bits in the bigint. + int bit_length() const noexcept { + int lz = ctlz(); + return int(limb_bits * vec.len()) - lz; + } + + bool mul(limb y) noexcept { + return small_mul(vec, y); + } + + bool add(limb y) noexcept { + return small_add(vec, y); + } + + // multiply as if by 2 raised to a power. + bool pow2(uint32_t exp) noexcept { + return shl(exp); + } + + // multiply as if by 5 raised to a power. + bool pow5(uint32_t exp) noexcept { + // multiply by a power of 5 + static constexpr uint32_t large_step = 135; + static constexpr uint64_t small_power_of_5[] = { + 1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL, + 1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL, + 6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL, + 3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL, + 2384185791015625UL, 11920928955078125UL, 59604644775390625UL, + 298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL, + }; +#ifdef FASTFLOAT_64BIT_LIMB + constexpr static limb large_power_of_5[] = { + 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL, + 10482974169319127550UL, 198276706040285095UL}; +#else + constexpr static limb large_power_of_5[] = { + 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U, + 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U}; +#endif + size_t large_length = sizeof(large_power_of_5) / sizeof(limb); + limb_span large = limb_span(large_power_of_5, large_length); + while (exp >= large_step) { + FASTFLOAT_TRY(large_mul(vec, large)); + exp -= large_step; + } +#ifdef FASTFLOAT_64BIT_LIMB + uint32_t small_step = 27; + limb max_native = 7450580596923828125UL; +#else + uint32_t small_step = 13; + limb max_native = 1220703125U; +#endif + while (exp >= small_step) { + FASTFLOAT_TRY(small_mul(vec, max_native)); + exp -= small_step; + } + if (exp != 0) { + FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp]))); + } + + return true; + } + + // multiply as if by 10 raised to a power. + bool pow10(uint32_t exp) noexcept { + FASTFLOAT_TRY(pow5(exp)); + return pow2(exp); + } +}; + +} // namespace fast_float + +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/decimal_to_binary.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/decimal_to_binary.h new file mode 100644 index 000000000..6da6c66a3 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/decimal_to_binary.h @@ -0,0 +1,194 @@ +#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H +#define FASTFLOAT_DECIMAL_TO_BINARY_H + +#include "float_common.h" +#include "fast_table.h" +#include <cfloat> +#include <cinttypes> +#include <cmath> +#include <cstdint> +#include <cstdlib> +#include <cstring> + +namespace fast_float { + +// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating +// the result, with the "high" part corresponding to the most significant bits and the +// low part corresponding to the least significant bits. +// +template <int bit_precision> +fastfloat_really_inline +value128 compute_product_approximation(int64_t q, uint64_t w) { + const int index = 2 * int(q - powers::smallest_power_of_five); + // For small values of q, e.g., q in [0,27], the answer is always exact because + // The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]); + // gives the exact answer. + value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]); + static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]"); + constexpr uint64_t precision_mask = (bit_precision < 64) ? + (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) + : uint64_t(0xFFFFFFFFFFFFFFFF); + if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower) + // regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed. + value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { + firstproduct.high++; + } + } + return firstproduct; +} + +namespace detail { +/** + * For q in (0,350), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * floor(p) + q + * where + * p = log(5**q)/log(2) = q * log(5)/log(2) + * + * For negative values of q in (-400,0), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * -ceil(p) + q + * where + * p = log(5**-q)/log(2) = -q * log(5)/log(2) + */ + constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { + return (((152170 + 65536) * q) >> 16) + 63; + } +} // namespace detail + +// create an adjusted mantissa, biased by the invalid power2 +// for significant digits already multiplied by 10 ** q. +template <typename binary> +fastfloat_really_inline +adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { + int hilz = int(w >> 63) ^ 1; + adjusted_mantissa answer; + answer.mantissa = w << hilz; + int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); + answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias); + return answer; +} + +// w * 10 ** q, without rounding the representation up. +// the power2 in the exponent will be adjusted by invalid_am_bias. +template <typename binary> +fastfloat_really_inline +adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept { + int lz = leading_zeroes(w); + w <<= lz; + value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); + return compute_error_scaled<binary>(q, product.high, lz); +} + +// w * 10 ** q +// The returned value should be a valid ieee64 number that simply need to be packed. +// However, in some very rare cases, the computation will fail. In such cases, we +// return an adjusted_mantissa with a negative power of 2: the caller should recompute +// in such cases. +template <typename binary> +fastfloat_really_inline +adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { + adjusted_mantissa answer; + if ((w == 0) || (q < binary::smallest_power_of_ten())) { + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + if (q > binary::largest_power_of_ten()) { + // we want to get infinity: + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + // At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five]. + + // We want the most significant bit of i to be 1. Shift if needed. + int lz = leading_zeroes(w); + w <<= lz; + + // The required precision is binary::mantissa_explicit_bits() + 3 because + // 1. We need the implicit bit + // 2. We need an extra bit for rounding purposes + // 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift) + + value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); + if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further + // In some very rare cases, this could happen, in which case we might need a more accurate + // computation that what we can provide cheaply. This is very, very unlikely. + // + const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0, + // and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation. + if(!inside_safe_exponent) { + return compute_error_scaled<binary>(q, product.high, lz); + } + } + // The "compute_product_approximation" function can be slightly slower than a branchless approach: + // value128 product = compute_product(q, w); + // but in practice, we can win big with the compute_product_approximation if its additional branch + // is easily predicted. Which is best is data specific. + int upperbit = int(product.high >> 63); + + answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); + + answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent()); + if (answer.power2 <= 0) { // we have a subnormal? + // Here have that answer.power2 <= 0 so -answer.power2 >= 0 + if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + // next line is safe because -answer.power2 + 1 < 64 + answer.mantissa >>= -answer.power2 + 1; + // Thankfully, we can't have both "round-to-even" and subnormals because + // "round-to-even" only occurs for powers close to 0. + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + // There is a weird scenario where we don't have a subnormal but just. + // Suppose we start with 2.2250738585072013e-308, we end up + // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal + // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round + // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer + // subnormal, but we can only know this after rounding. + // So we only declare a subnormal if we are smaller than the threshold. + answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1; + return answer; + } + + // usually, we round *up*, but if we fall right in between and and we have an + // even basis, we need to round down + // We are only concerned with the cases where 5**q fits in single 64-bit word. + if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) && + ((answer.mantissa & 3) == 1) ) { // we may fall between two floats! + // To be in-between two floats we need that in doing + // answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); + // ... we dropped out only zeroes. But if this happened, then we can go back!!! + if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) { + answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up + } + } + + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) { + answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); + answer.power2++; // undo previous addition + } + + answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); + if (answer.power2 >= binary::infinite_power()) { // infinity + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + } + return answer; +} + +} // namespace fast_float + +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/digit_comparison.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/digit_comparison.h new file mode 100644 index 000000000..7ffe87430 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/digit_comparison.h @@ -0,0 +1,423 @@ +#ifndef FASTFLOAT_DIGIT_COMPARISON_H +#define FASTFLOAT_DIGIT_COMPARISON_H + +#include <algorithm> +#include <cstdint> +#include <cstring> +#include <iterator> + +#include "float_common.h" +#include "bigint.h" +#include "ascii_number.h" + +namespace fast_float { + +// 1e0 to 1e19 +constexpr static uint64_t powers_of_ten_uint64[] = { + 1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL, + 1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL, + 100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL, + 1000000000000000000UL, 10000000000000000000UL}; + +// calculate the exponent, in scientific notation, of the number. +// this algorithm is not even close to optimized, but it has no practical +// effect on performance: in order to have a faster algorithm, we'd need +// to slow down performance for faster algorithms, and this is still fast. +fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept { + uint64_t mantissa = num.mantissa; + int32_t exponent = int32_t(num.exponent); + while (mantissa >= 10000) { + mantissa /= 10000; + exponent += 4; + } + while (mantissa >= 100) { + mantissa /= 100; + exponent += 2; + } + while (mantissa >= 10) { + mantissa /= 10; + exponent += 1; + } + return exponent; +} + +// this converts a native floating-point number to an extended-precision float. +template <typename T> +fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { + adjusted_mantissa am; + int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent(); + if (std::is_same<T, float>::value) { + constexpr uint32_t exponent_mask = 0x7F800000; + constexpr uint32_t mantissa_mask = 0x007FFFFF; + constexpr uint64_t hidden_bit_mask = 0x00800000; + uint32_t bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; + } else { + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + } + } else { + constexpr uint64_t exponent_mask = 0x7FF0000000000000; + constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF; + constexpr uint64_t hidden_bit_mask = 0x0010000000000000; + uint64_t bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; + } else { + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + } + } + + return am; +} + +// get the extended precision value of the halfway point between b and b+u. +// we are given a native float that represents b, so we need to adjust it +// halfway between b and b+u. +template <typename T> +fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept { + adjusted_mantissa am = to_extended(value); + am.mantissa <<= 1; + am.mantissa += 1; + am.power2 -= 1; + return am; +} + +// round an extended-precision float to the nearest machine float. +template <typename T, typename callback> +fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept { + int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; + if (-am.power2 >= mantissa_shift) { + // have a denormal float + int32_t shift = -am.power2 + 1; + cb(am, std::min(shift, 64)); + // check for round-up: if rounding-nearest carried us to the hidden bit. + am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1; + return; + } + + // have a normal float, use the default shift. + cb(am, mantissa_shift); + + // check for carry + if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) { + am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); + am.power2++; + } + + // check for infinite: we could have carried to an infinite power + am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); + if (am.power2 >= binary_format<T>::infinite_power()) { + am.power2 = binary_format<T>::infinite_power(); + am.mantissa = 0; + } +} + +template <typename callback> +fastfloat_really_inline +void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept { + uint64_t mask; + uint64_t halfway; + if (shift == 64) { + mask = UINT64_MAX; + } else { + mask = (uint64_t(1) << shift) - 1; + } + if (shift == 0) { + halfway = 0; + } else { + halfway = uint64_t(1) << (shift - 1); + } + uint64_t truncated_bits = am.mantissa & mask; + uint64_t is_above = truncated_bits > halfway; + uint64_t is_halfway = truncated_bits == halfway; + + // shift digits into position + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; + + bool is_odd = (am.mantissa & 1) == 1; + am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); +} + +fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept { + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; +} + +fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept { + uint64_t val; + while (std::distance(first, last) >= 8) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != 0x3030303030303030) { + break; + } + first += 8; + } + while (first != last) { + if (*first != '0') { + break; + } + first++; + } +} + +// determine if any non-zero digits were truncated. +// all characters must be valid digits. +fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept { + // do 8-bit optimizations, can just compare to 8 literal 0s. + uint64_t val; + while (std::distance(first, last) >= 8) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != 0x3030303030303030) { + return true; + } + first += 8; + } + while (first != last) { + if (*first != '0') { + return true; + } + first++; + } + return false; +} + +fastfloat_really_inline bool is_truncated(byte_span s) noexcept { + return is_truncated(s.ptr, s.ptr + s.len()); +} + +fastfloat_really_inline +void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { + value = value * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + counter += 8; + count += 8; +} + +fastfloat_really_inline +void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { + value = value * 10 + limb(*p - '0'); + p++; + counter++; + count++; +} + +fastfloat_really_inline +void add_native(bigint& big, limb power, limb value) noexcept { + big.mul(power); + big.add(value); +} + +fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcept { + // need to round-up the digits, but need to avoid rounding + // ....9999 to ...10000, which could cause a false halfway point. + add_native(big, 10, 1); + count++; +} + +// parse the significant digits into a big integer +inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept { + // try to minimize the number of big integer and scalar multiplication. + // therefore, try to parse 8 digits at a time, and multiply by the largest + // scalar value (9 or 19 digits) for each step. + size_t counter = 0; + digits = 0; + limb value = 0; +#ifdef FASTFLOAT_64BIT_LIMB + size_t step = 19; +#else + size_t step = 9; +#endif + + // process all integer digits. + const char* p = num.integer.ptr; + const char* pend = p + num.integer.len(); + skip_zeros(p, pend); + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (num.fraction.ptr != nullptr) { + truncated |= is_truncated(num.fraction); + } + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + + // add our fraction digits, if they're available. + if (num.fraction.ptr != nullptr) { + p = num.fraction.ptr; + pend = p + num.fraction.len(); + if (digits == 0) { + skip_zeros(p, pend); + } + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + } + + if (counter != 0) { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + } +} + +template <typename T> +inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept { + FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); + adjusted_mantissa answer; + bool truncated; + answer.mantissa = bigmant.hi64(truncated); + int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent(); + answer.power2 = bigmant.bit_length() - 64 + bias; + + round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) { + round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { + return is_above || (is_halfway && truncated) || (is_odd && is_halfway); + }); + }); + + return answer; +} + +// the scaling here is quite simple: we have, for the real digits `m * 10^e`, +// and for the theoretical digits `n * 2^f`. Since `e` is always negative, +// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`. +// we then need to scale by `2^(f- e)`, and then the two significant digits +// are of the same magnitude. +template <typename T> +inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept { + bigint& real_digits = bigmant; + int32_t real_exp = exponent; + + // get the value of `b`, rounded down, and get a bigint representation of b+h + adjusted_mantissa am_b = am; + // gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type. + round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); }); + T b; + to_float(false, am_b, b); + adjusted_mantissa theor = to_extended_halfway(b); + bigint theor_digits(theor.mantissa); + int32_t theor_exp = theor.power2; + + // scale real digits and theor digits to be same power. + int32_t pow2_exp = theor_exp - real_exp; + uint32_t pow5_exp = uint32_t(-real_exp); + if (pow5_exp != 0) { + FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); + } + if (pow2_exp > 0) { + FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); + } else if (pow2_exp < 0) { + FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); + } + + // compare digits, and use it to director rounding + int ord = real_digits.compare(theor_digits); + adjusted_mantissa answer = am; + round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) { + round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool { + (void)_; // not needed, since we've done our comparison + (void)__; // not needed, since we've done our comparison + if (ord > 0) { + return true; + } else if (ord < 0) { + return false; + } else { + return is_odd; + } + }); + }); + + return answer; +} + +// parse the significant digits as a big integer to unambiguously round the +// the significant digits. here, we are trying to determine how to round +// an extended float representation close to `b+h`, halfway between `b` +// (the float rounded-down) and `b+u`, the next positive float. this +// algorithm is always correct, and uses one of two approaches. when +// the exponent is positive relative to the significant digits (such as +// 1234), we create a big-integer representation, get the high 64-bits, +// determine if any lower bits are truncated, and use that to direct +// rounding. in case of a negative exponent relative to the significant +// digits (such as 1.2345), we create a theoretical representation of +// `b` as a big-integer type, scaled to the same binary exponent as +// the actual digits. we then compare the big integer representations +// of both, and use that to direct rounding. +template <typename T> +inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept { + // remove the invalid exponent bias + am.power2 -= invalid_am_bias; + + int32_t sci_exp = scientific_exponent(num); + size_t max_digits = binary_format<T>::max_digits(); + size_t digits = 0; + bigint bigmant; + parse_mantissa(bigmant, num, max_digits, digits); + // can't underflow, since digits is at most max_digits. + int32_t exponent = sci_exp + 1 - int32_t(digits); + if (exponent >= 0) { + return positive_digit_comp<T>(bigmant, exponent); + } else { + return negative_digit_comp<T>(bigmant, am, exponent); + } +} + +} // namespace fast_float + +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/fast_float.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/fast_float.h new file mode 100644 index 000000000..3c483803a --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/fast_float.h @@ -0,0 +1,63 @@ +#ifndef FASTFLOAT_FAST_FLOAT_H +#define FASTFLOAT_FAST_FLOAT_H + +#include <system_error> + +namespace fast_float { +enum chars_format { + scientific = 1<<0, + fixed = 1<<2, + hex = 1<<3, + general = fixed | scientific +}; + + +struct from_chars_result { + const char *ptr; + std::errc ec; +}; + +struct parse_options { + constexpr explicit parse_options(chars_format fmt = chars_format::general, + char dot = '.') + : format(fmt), decimal_point(dot) {} + + /** Which number formats are accepted */ + chars_format format; + /** The character used as decimal point */ + char decimal_point; +}; + +/** + * This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting + * a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale. + * The resulting floating-point value is the closest floating-point values (using either float or double), + * using the "round to even" convention for values that would otherwise fall right in-between two values. + * That is, we provide exact parsing according to the IEEE standard. + * + * Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the + * parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned + * `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored. + * + * The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`). + * + * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of + * the type `fast_float::chars_format`. It is a bitset value: we check whether + * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set + * to determine whether we allowe the fixed point and scientific notation respectively. + * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. + */ +template<typename T> +from_chars_result from_chars(const char *first, const char *last, + T &value, chars_format fmt = chars_format::general) noexcept; + +/** + * Like from_chars, but accepts an `options` argument to govern number parsing. + */ +template<typename T> +from_chars_result from_chars_advanced(const char *first, const char *last, + T &value, parse_options options) noexcept; + +} +#include "parse_number.h" +#endif // FASTFLOAT_FAST_FLOAT_H diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/fast_table.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/fast_table.h new file mode 100644 index 000000000..5766274ca --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/fast_table.h @@ -0,0 +1,699 @@ +#ifndef FASTFLOAT_FAST_TABLE_H +#define FASTFLOAT_FAST_TABLE_H + +#include <cstdint> + +namespace fast_float { + +/** + * When mapping numbers from decimal to binary, + * we go from w * 10^q to m * 2^p but we have + * 10^q = 5^q * 2^q, so effectively + * we are trying to match + * w * 2^q * 5^q to m * 2^p. Thus the powers of two + * are not a concern since they can be represented + * exactly using the binary notation, only the powers of five + * affect the binary significand. + */ + +/** + * The smallest non-zero float (binary64) is 2^−1074. + * We take as input numbers of the form w x 10^q where w < 2^64. + * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. + * However, we have that + * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^−1074. + * Thus it is possible for a number of the form w * 10^-342 where + * w is a 64-bit value to be a non-zero floating-point number. + ********* + * Any number of form w * 10^309 where w>= 1 is going to be + * infinite in binary64 so we never need to worry about powers + * of 5 greater than 308. + */ +template <class unused = void> +struct powers_template { + +constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten(); +constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten(); +constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1); +// Powers of five from 5^-342 all the way to 5^308 rounded toward one. +static const uint64_t power_of_five_128[number_of_entries]; +}; + +template <class unused> +const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = { + 0xeef453d6923bd65a,0x113faa2906a13b3f, + 0x9558b4661b6565f8,0x4ac7ca59a424c507, + 0xbaaee17fa23ebf76,0x5d79bcf00d2df649, + 0xe95a99df8ace6f53,0xf4d82c2c107973dc, + 0x91d8a02bb6c10594,0x79071b9b8a4be869, + 0xb64ec836a47146f9,0x9748e2826cdee284, + 0xe3e27a444d8d98b7,0xfd1b1b2308169b25, + 0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7, + 0xb208ef855c969f4f,0xbdbd2d335e51a935, + 0xde8b2b66b3bc4723,0xad2c788035e61382, + 0x8b16fb203055ac76,0x4c3bcb5021afcc31, + 0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d, + 0xd953e8624b85dd78,0xd71d6dad34a2af0d, + 0x87d4713d6f33aa6b,0x8672648c40e5ad68, + 0xa9c98d8ccb009506,0x680efdaf511f18c2, + 0xd43bf0effdc0ba48,0x212bd1b2566def2, + 0x84a57695fe98746d,0x14bb630f7604b57, + 0xa5ced43b7e3e9188,0x419ea3bd35385e2d, + 0xcf42894a5dce35ea,0x52064cac828675b9, + 0x818995ce7aa0e1b2,0x7343efebd1940993, + 0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8, + 0xca66fa129f9b60a6,0xd41a26e077774ef6, + 0xfd00b897478238d0,0x8920b098955522b4, + 0x9e20735e8cb16382,0x55b46e5f5d5535b0, + 0xc5a890362fddbc62,0xeb2189f734aa831d, + 0xf712b443bbd52b7b,0xa5e9ec7501d523e4, + 0x9a6bb0aa55653b2d,0x47b233c92125366e, + 0xc1069cd4eabe89f8,0x999ec0bb696e840a, + 0xf148440a256e2c76,0xc00670ea43ca250d, + 0x96cd2a865764dbca,0x380406926a5e5728, + 0xbc807527ed3e12bc,0xc605083704f5ecf2, + 0xeba09271e88d976b,0xf7864a44c633682e, + 0x93445b8731587ea3,0x7ab3ee6afbe0211d, + 0xb8157268fdae9e4c,0x5960ea05bad82964, + 0xe61acf033d1a45df,0x6fb92487298e33bd, + 0x8fd0c16206306bab,0xa5d3b6d479f8e056, + 0xb3c4f1ba87bc8696,0x8f48a4899877186c, + 0xe0b62e2929aba83c,0x331acdabfe94de87, + 0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14, + 0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9, + 0xdb71e91432b1a24a,0xc9e82cd9f69d6150, + 0x892731ac9faf056e,0xbe311c083a225cd2, + 0xab70fe17c79ac6ca,0x6dbd630a48aaf406, + 0xd64d3d9db981787d,0x92cbbccdad5b108, + 0x85f0468293f0eb4e,0x25bbf56008c58ea5, + 0xa76c582338ed2621,0xaf2af2b80af6f24e, + 0xd1476e2c07286faa,0x1af5af660db4aee1, + 0x82cca4db847945ca,0x50d98d9fc890ed4d, + 0xa37fce126597973c,0xe50ff107bab528a0, + 0xcc5fc196fefd7d0c,0x1e53ed49a96272c8, + 0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a, + 0x9faacf3df73609b1,0x77b191618c54e9ac, + 0xc795830d75038c1d,0xd59df5b9ef6a2417, + 0xf97ae3d0d2446f25,0x4b0573286b44ad1d, + 0x9becce62836ac577,0x4ee367f9430aec32, + 0xc2e801fb244576d5,0x229c41f793cda73f, + 0xf3a20279ed56d48a,0x6b43527578c1110f, + 0x9845418c345644d6,0x830a13896b78aaa9, + 0xbe5691ef416bd60c,0x23cc986bc656d553, + 0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8, + 0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9, + 0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53, + 0xe858ad248f5c22c9,0xd1b3400f8f9cff68, + 0x91376c36d99995be,0x23100809b9c21fa1, + 0xb58547448ffffb2d,0xabd40a0c2832a78a, + 0xe2e69915b3fff9f9,0x16c90c8f323f516c, + 0x8dd01fad907ffc3b,0xae3da7d97f6792e3, + 0xb1442798f49ffb4a,0x99cd11cfdf41779c, + 0xdd95317f31c7fa1d,0x40405643d711d583, + 0x8a7d3eef7f1cfc52,0x482835ea666b2572, + 0xad1c8eab5ee43b66,0xda3243650005eecf, + 0xd863b256369d4a40,0x90bed43e40076a82, + 0x873e4f75e2224e68,0x5a7744a6e804a291, + 0xa90de3535aaae202,0x711515d0a205cb36, + 0xd3515c2831559a83,0xd5a5b44ca873e03, + 0x8412d9991ed58091,0xe858790afe9486c2, + 0xa5178fff668ae0b6,0x626e974dbe39a872, + 0xce5d73ff402d98e3,0xfb0a3d212dc8128f, + 0x80fa687f881c7f8e,0x7ce66634bc9d0b99, + 0xa139029f6a239f72,0x1c1fffc1ebc44e80, + 0xc987434744ac874e,0xa327ffb266b56220, + 0xfbe9141915d7a922,0x4bf1ff9f0062baa8, + 0x9d71ac8fada6c9b5,0x6f773fc3603db4a9, + 0xc4ce17b399107c22,0xcb550fb4384d21d3, + 0xf6019da07f549b2b,0x7e2a53a146606a48, + 0x99c102844f94e0fb,0x2eda7444cbfc426d, + 0xc0314325637a1939,0xfa911155fefb5308, + 0xf03d93eebc589f88,0x793555ab7eba27ca, + 0x96267c7535b763b5,0x4bc1558b2f3458de, + 0xbbb01b9283253ca2,0x9eb1aaedfb016f16, + 0xea9c227723ee8bcb,0x465e15a979c1cadc, + 0x92a1958a7675175f,0xbfacd89ec191ec9, + 0xb749faed14125d36,0xcef980ec671f667b, + 0xe51c79a85916f484,0x82b7e12780e7401a, + 0x8f31cc0937ae58d2,0xd1b2ecb8b0908810, + 0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15, + 0xdfbdcece67006ac9,0x67a791e093e1d49a, + 0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0, + 0xaecc49914078536d,0x58fae9f773886e18, + 0xda7f5bf590966848,0xaf39a475506a899e, + 0x888f99797a5e012d,0x6d8406c952429603, + 0xaab37fd7d8f58178,0xc8e5087ba6d33b83, + 0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64, + 0x855c3be0a17fcd26,0x5cf2eea09a55067f, + 0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e, + 0xd0601d8efc57b08b,0xf13b94daf124da26, + 0x823c12795db6ce57,0x76c53d08d6b70858, + 0xa2cb1717b52481ed,0x54768c4b0c64ca6e, + 0xcb7ddcdda26da268,0xa9942f5dcf7dfd09, + 0xfe5d54150b090b02,0xd3f93b35435d7c4c, + 0x9efa548d26e5a6e1,0xc47bc5014a1a6daf, + 0xc6b8e9b0709f109a,0x359ab6419ca1091b, + 0xf867241c8cc6d4c0,0xc30163d203c94b62, + 0x9b407691d7fc44f8,0x79e0de63425dcf1d, + 0xc21094364dfb5636,0x985915fc12f542e4, + 0xf294b943e17a2bc4,0x3e6f5b7b17b2939d, + 0x979cf3ca6cec5b5a,0xa705992ceecf9c42, + 0xbd8430bd08277231,0x50c6ff782a838353, + 0xece53cec4a314ebd,0xa4f8bf5635246428, + 0x940f4613ae5ed136,0x871b7795e136be99, + 0xb913179899f68584,0x28e2557b59846e3f, + 0xe757dd7ec07426e5,0x331aeada2fe589cf, + 0x9096ea6f3848984f,0x3ff0d2c85def7621, + 0xb4bca50b065abe63,0xfed077a756b53a9, + 0xe1ebce4dc7f16dfb,0xd3e8495912c62894, + 0x8d3360f09cf6e4bd,0x64712dd7abbbd95c, + 0xb080392cc4349dec,0xbd8d794d96aacfb3, + 0xdca04777f541c567,0xecf0d7a0fc5583a0, + 0x89e42caaf9491b60,0xf41686c49db57244, + 0xac5d37d5b79b6239,0x311c2875c522ced5, + 0xd77485cb25823ac7,0x7d633293366b828b, + 0x86a8d39ef77164bc,0xae5dff9c02033197, + 0xa8530886b54dbdeb,0xd9f57f830283fdfc, + 0xd267caa862a12d66,0xd072df63c324fd7b, + 0x8380dea93da4bc60,0x4247cb9e59f71e6d, + 0xa46116538d0deb78,0x52d9be85f074e608, + 0xcd795be870516656,0x67902e276c921f8b, + 0x806bd9714632dff6,0xba1cd8a3db53b6, + 0xa086cfcd97bf97f3,0x80e8a40eccd228a4, + 0xc8a883c0fdaf7df0,0x6122cd128006b2cd, + 0xfad2a4b13d1b5d6c,0x796b805720085f81, + 0x9cc3a6eec6311a63,0xcbe3303674053bb0, + 0xc3f490aa77bd60fc,0xbedbfc4411068a9c, + 0xf4f1b4d515acb93b,0xee92fb5515482d44, + 0x991711052d8bf3c5,0x751bdd152d4d1c4a, + 0xbf5cd54678eef0b6,0xd262d45a78a0635d, + 0xef340a98172aace4,0x86fb897116c87c34, + 0x9580869f0e7aac0e,0xd45d35e6ae3d4da0, + 0xbae0a846d2195712,0x8974836059cca109, + 0xe998d258869facd7,0x2bd1a438703fc94b, + 0x91ff83775423cc06,0x7b6306a34627ddcf, + 0xb67f6455292cbf08,0x1a3bc84c17b1d542, + 0xe41f3d6a7377eeca,0x20caba5f1d9e4a93, + 0x8e938662882af53e,0x547eb47b7282ee9c, + 0xb23867fb2a35b28d,0xe99e619a4f23aa43, + 0xdec681f9f4c31f31,0x6405fa00e2ec94d4, + 0x8b3c113c38f9f37e,0xde83bc408dd3dd04, + 0xae0b158b4738705e,0x9624ab50b148d445, + 0xd98ddaee19068c76,0x3badd624dd9b0957, + 0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6, + 0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c, + 0xd47487cc8470652b,0x7647c3200069671f, + 0x84c8d4dfd2c63f3b,0x29ecd9f40041e073, + 0xa5fb0a17c777cf09,0xf468107100525890, + 0xcf79cc9db955c2cc,0x7182148d4066eeb4, + 0x81ac1fe293d599bf,0xc6f14cd848405530, + 0xa21727db38cb002f,0xb8ada00e5a506a7c, + 0xca9cf1d206fdc03b,0xa6d90811f0e4851c, + 0xfd442e4688bd304a,0x908f4a166d1da663, + 0x9e4a9cec15763e2e,0x9a598e4e043287fe, + 0xc5dd44271ad3cdba,0x40eff1e1853f29fd, + 0xf7549530e188c128,0xd12bee59e68ef47c, + 0x9a94dd3e8cf578b9,0x82bb74f8301958ce, + 0xc13a148e3032d6e7,0xe36a52363c1faf01, + 0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1, + 0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9, + 0xbcb2b812db11a5de,0x7415d448f6b6f0e7, + 0xebdf661791d60f56,0x111b495b3464ad21, + 0x936b9fcebb25c995,0xcab10dd900beec34, + 0xb84687c269ef3bfb,0x3d5d514f40eea742, + 0xe65829b3046b0afa,0xcb4a5a3112a5112, + 0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab, + 0xb3f4e093db73a093,0x59ed216765690f56, + 0xe0f218b8d25088b8,0x306869c13ec3532c, + 0x8c974f7383725573,0x1e414218c73a13fb, + 0xafbd2350644eeacf,0xe5d1929ef90898fa, + 0xdbac6c247d62a583,0xdf45f746b74abf39, + 0x894bc396ce5da772,0x6b8bba8c328eb783, + 0xab9eb47c81f5114f,0x66ea92f3f326564, + 0xd686619ba27255a2,0xc80a537b0efefebd, + 0x8613fd0145877585,0xbd06742ce95f5f36, + 0xa798fc4196e952e7,0x2c48113823b73704, + 0xd17f3b51fca3a7a0,0xf75a15862ca504c5, + 0x82ef85133de648c4,0x9a984d73dbe722fb, + 0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba, + 0xcc963fee10b7d1b3,0x318df905079926a8, + 0xffbbcfe994e5c61f,0xfdf17746497f7052, + 0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633, + 0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0, + 0xf9bd690a1b68637b,0x3dfdce7aa3c673b0, + 0x9c1661a651213e2d,0x6bea10ca65c084e, + 0xc31bfa0fe5698db8,0x486e494fcff30a62, + 0xf3e2f893dec3f126,0x5a89dba3c3efccfa, + 0x986ddb5c6b3a76b7,0xf89629465a75e01c, + 0xbe89523386091465,0xf6bbb397f1135823, + 0xee2ba6c0678b597f,0x746aa07ded582e2c, + 0x94db483840b717ef,0xa8c2a44eb4571cdc, + 0xba121a4650e4ddeb,0x92f34d62616ce413, + 0xe896a0d7e51e1566,0x77b020baf9c81d17, + 0x915e2486ef32cd60,0xace1474dc1d122e, + 0xb5b5ada8aaff80b8,0xd819992132456ba, + 0xe3231912d5bf60e6,0x10e1fff697ed6c69, + 0x8df5efabc5979c8f,0xca8d3ffa1ef463c1, + 0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2, + 0xddd0467c64bce4a0,0xac7cb3f6d05ddbde, + 0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b, + 0xad4ab7112eb3929d,0x86c16c98d2c953c6, + 0xd89d64d57a607744,0xe871c7bf077ba8b7, + 0x87625f056c7c4a8b,0x11471cd764ad4972, + 0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf, + 0xd389b47879823479,0x4aff1d108d4ec2c3, + 0x843610cb4bf160cb,0xcedf722a585139ba, + 0xa54394fe1eedb8fe,0xc2974eb4ee658828, + 0xce947a3da6a9273e,0x733d226229feea32, + 0x811ccc668829b887,0x806357d5a3f525f, + 0xa163ff802a3426a8,0xca07c2dcb0cf26f7, + 0xc9bcff6034c13052,0xfc89b393dd02f0b5, + 0xfc2c3f3841f17c67,0xbbac2078d443ace2, + 0x9d9ba7832936edc0,0xd54b944b84aa4c0d, + 0xc5029163f384a931,0xa9e795e65d4df11, + 0xf64335bcf065d37d,0x4d4617b5ff4a16d5, + 0x99ea0196163fa42e,0x504bced1bf8e4e45, + 0xc06481fb9bcf8d39,0xe45ec2862f71e1d6, + 0xf07da27a82c37088,0x5d767327bb4e5a4c, + 0x964e858c91ba2655,0x3a6a07f8d510f86f, + 0xbbe226efb628afea,0x890489f70a55368b, + 0xeadab0aba3b2dbe5,0x2b45ac74ccea842e, + 0x92c8ae6b464fc96f,0x3b0b8bc90012929d, + 0xb77ada0617e3bbcb,0x9ce6ebb40173744, + 0xe55990879ddcaabd,0xcc420a6a101d0515, + 0x8f57fa54c2a9eab6,0x9fa946824a12232d, + 0xb32df8e9f3546564,0x47939822dc96abf9, + 0xdff9772470297ebd,0x59787e2b93bc56f7, + 0x8bfbea76c619ef36,0x57eb4edb3c55b65a, + 0xaefae51477a06b03,0xede622920b6b23f1, + 0xdab99e59958885c4,0xe95fab368e45eced, + 0x88b402f7fd75539b,0x11dbcb0218ebb414, + 0xaae103b5fcd2a881,0xd652bdc29f26a119, + 0xd59944a37c0752a2,0x4be76d3346f0495f, + 0x857fcae62d8493a5,0x6f70a4400c562ddb, + 0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952, + 0xd097ad07a71f26b2,0x7e2000a41346a7a7, + 0x825ecc24c873782f,0x8ed400668c0c28c8, + 0xa2f67f2dfa90563b,0x728900802f0f32fa, + 0xcbb41ef979346bca,0x4f2b40a03ad2ffb9, + 0xfea126b7d78186bc,0xe2f610c84987bfa8, + 0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9, + 0xc6ede63fa05d3143,0x91503d1c79720dbb, + 0xf8a95fcf88747d94,0x75a44c6397ce912a, + 0x9b69dbe1b548ce7c,0xc986afbe3ee11aba, + 0xc24452da229b021b,0xfbe85badce996168, + 0xf2d56790ab41c2a2,0xfae27299423fb9c3, + 0x97c560ba6b0919a5,0xdccd879fc967d41a, + 0xbdb6b8e905cb600f,0x5400e987bbc1c920, + 0xed246723473e3813,0x290123e9aab23b68, + 0x9436c0760c86e30b,0xf9a0b6720aaf6521, + 0xb94470938fa89bce,0xf808e40e8d5b3e69, + 0xe7958cb87392c2c2,0xb60b1d1230b20e04, + 0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2, + 0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3, + 0xe2280b6c20dd5232,0x25c6da63c38de1b0, + 0x8d590723948a535f,0x579c487e5a38ad0e, + 0xb0af48ec79ace837,0x2d835a9df0c6d851, + 0xdcdb1b2798182244,0xf8e431456cf88e65, + 0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff, + 0xac8b2d36eed2dac5,0xe272467e3d222f3f, + 0xd7adf884aa879177,0x5b0ed81dcc6abb0f, + 0x86ccbb52ea94baea,0x98e947129fc2b4e9, + 0xa87fea27a539e9a5,0x3f2398d747b36224, + 0xd29fe4b18e88640e,0x8eec7f0d19a03aad, + 0x83a3eeeef9153e89,0x1953cf68300424ac, + 0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7, + 0xcdb02555653131b6,0x3792f412cb06794d, + 0x808e17555f3ebf11,0xe2bbd88bbee40bd0, + 0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4, + 0xc8de047564d20a8b,0xf245825a5a445275, + 0xfb158592be068d2e,0xeed6e2f0f0d56712, + 0x9ced737bb6c4183d,0x55464dd69685606b, + 0xc428d05aa4751e4c,0xaa97e14c3c26b886, + 0xf53304714d9265df,0xd53dd99f4b3066a8, + 0x993fe2c6d07b7fab,0xe546a8038efe4029, + 0xbf8fdb78849a5f96,0xde98520472bdd033, + 0xef73d256a5c0f77c,0x963e66858f6d4440, + 0x95a8637627989aad,0xdde7001379a44aa8, + 0xbb127c53b17ec159,0x5560c018580d5d52, + 0xe9d71b689dde71af,0xaab8f01e6e10b4a6, + 0x9226712162ab070d,0xcab3961304ca70e8, + 0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22, + 0xe45c10c42a2b3b05,0x8cb89a7db77c506a, + 0x8eb98a7a9a5b04e3,0x77f3608e92adb242, + 0xb267ed1940f1c61c,0x55f038b237591ed3, + 0xdf01e85f912e37a3,0x6b6c46dec52f6688, + 0x8b61313bbabce2c6,0x2323ac4b3b3da015, + 0xae397d8aa96c1b77,0xabec975e0a0d081a, + 0xd9c7dced53c72255,0x96e7bd358c904a21, + 0x881cea14545c7575,0x7e50d64177da2e54, + 0xaa242499697392d2,0xdde50bd1d5d0b9e9, + 0xd4ad2dbfc3d07787,0x955e4ec64b44e864, + 0x84ec3c97da624ab4,0xbd5af13bef0b113e, + 0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e, + 0xcfb11ead453994ba,0x67de18eda5814af2, + 0x81ceb32c4b43fcf4,0x80eacf948770ced7, + 0xa2425ff75e14fc31,0xa1258379a94d028d, + 0xcad2f7f5359a3b3e,0x96ee45813a04330, + 0xfd87b5f28300ca0d,0x8bca9d6e188853fc, + 0x9e74d1b791e07e48,0x775ea264cf55347e, + 0xc612062576589dda,0x95364afe032a819e, + 0xf79687aed3eec551,0x3a83ddbd83f52205, + 0x9abe14cd44753b52,0xc4926a9672793543, + 0xc16d9a0095928a27,0x75b7053c0f178294, + 0xf1c90080baf72cb1,0x5324c68b12dd6339, + 0x971da05074da7bee,0xd3f6fc16ebca5e04, + 0xbce5086492111aea,0x88f4bb1ca6bcf585, + 0xec1e4a7db69561a5,0x2b31e9e3d06c32e6, + 0x9392ee8e921d5d07,0x3aff322e62439fd0, + 0xb877aa3236a4b449,0x9befeb9fad487c3, + 0xe69594bec44de15b,0x4c2ebe687989a9b4, + 0x901d7cf73ab0acd9,0xf9d37014bf60a11, + 0xb424dc35095cd80f,0x538484c19ef38c95, + 0xe12e13424bb40e13,0x2865a5f206b06fba, + 0x8cbccc096f5088cb,0xf93f87b7442e45d4, + 0xafebff0bcb24aafe,0xf78f69a51539d749, + 0xdbe6fecebdedd5be,0xb573440e5a884d1c, + 0x89705f4136b4a597,0x31680a88f8953031, + 0xabcc77118461cefc,0xfdc20d2b36ba7c3e, + 0xd6bf94d5e57a42bc,0x3d32907604691b4d, + 0x8637bd05af6c69b5,0xa63f9a49c2c1b110, + 0xa7c5ac471b478423,0xfcf80dc33721d54, + 0xd1b71758e219652b,0xd3c36113404ea4a9, + 0x83126e978d4fdf3b,0x645a1cac083126ea, + 0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4, + 0xcccccccccccccccc,0xcccccccccccccccd, + 0x8000000000000000,0x0, + 0xa000000000000000,0x0, + 0xc800000000000000,0x0, + 0xfa00000000000000,0x0, + 0x9c40000000000000,0x0, + 0xc350000000000000,0x0, + 0xf424000000000000,0x0, + 0x9896800000000000,0x0, + 0xbebc200000000000,0x0, + 0xee6b280000000000,0x0, + 0x9502f90000000000,0x0, + 0xba43b74000000000,0x0, + 0xe8d4a51000000000,0x0, + 0x9184e72a00000000,0x0, + 0xb5e620f480000000,0x0, + 0xe35fa931a0000000,0x0, + 0x8e1bc9bf04000000,0x0, + 0xb1a2bc2ec5000000,0x0, + 0xde0b6b3a76400000,0x0, + 0x8ac7230489e80000,0x0, + 0xad78ebc5ac620000,0x0, + 0xd8d726b7177a8000,0x0, + 0x878678326eac9000,0x0, + 0xa968163f0a57b400,0x0, + 0xd3c21bcecceda100,0x0, + 0x84595161401484a0,0x0, + 0xa56fa5b99019a5c8,0x0, + 0xcecb8f27f4200f3a,0x0, + 0x813f3978f8940984,0x4000000000000000, + 0xa18f07d736b90be5,0x5000000000000000, + 0xc9f2c9cd04674ede,0xa400000000000000, + 0xfc6f7c4045812296,0x4d00000000000000, + 0x9dc5ada82b70b59d,0xf020000000000000, + 0xc5371912364ce305,0x6c28000000000000, + 0xf684df56c3e01bc6,0xc732000000000000, + 0x9a130b963a6c115c,0x3c7f400000000000, + 0xc097ce7bc90715b3,0x4b9f100000000000, + 0xf0bdc21abb48db20,0x1e86d40000000000, + 0x96769950b50d88f4,0x1314448000000000, + 0xbc143fa4e250eb31,0x17d955a000000000, + 0xeb194f8e1ae525fd,0x5dcfab0800000000, + 0x92efd1b8d0cf37be,0x5aa1cae500000000, + 0xb7abc627050305ad,0xf14a3d9e40000000, + 0xe596b7b0c643c719,0x6d9ccd05d0000000, + 0x8f7e32ce7bea5c6f,0xe4820023a2000000, + 0xb35dbf821ae4f38b,0xdda2802c8a800000, + 0xe0352f62a19e306e,0xd50b2037ad200000, + 0x8c213d9da502de45,0x4526f422cc340000, + 0xaf298d050e4395d6,0x9670b12b7f410000, + 0xdaf3f04651d47b4c,0x3c0cdd765f114000, + 0x88d8762bf324cd0f,0xa5880a69fb6ac800, + 0xab0e93b6efee0053,0x8eea0d047a457a00, + 0xd5d238a4abe98068,0x72a4904598d6d880, + 0x85a36366eb71f041,0x47a6da2b7f864750, + 0xa70c3c40a64e6c51,0x999090b65f67d924, + 0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d, + 0x82818f1281ed449f,0xbff8f10e7a8921a4, + 0xa321f2d7226895c7,0xaff72d52192b6a0d, + 0xcbea6f8ceb02bb39,0x9bf4f8a69f764490, + 0xfee50b7025c36a08,0x2f236d04753d5b4, + 0x9f4f2726179a2245,0x1d762422c946590, + 0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5, + 0xf8ebad2b84e0d58b,0xd2e0898765a7deb2, + 0x9b934c3b330c8577,0x63cc55f49f88eb2f, + 0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb, + 0xf316271c7fc3908a,0x8bef464e3945ef7a, + 0x97edd871cfda3a56,0x97758bf0e3cbb5ac, + 0xbde94e8e43d0c8ec,0x3d52eeed1cbea317, + 0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd, + 0x945e455f24fb1cf8,0x8fe8caa93e74ef6a, + 0xb975d6b6ee39e436,0xb3e2fd538e122b44, + 0xe7d34c64a9c85d44,0x60dbbca87196b616, + 0x90e40fbeea1d3a4a,0xbc8955e946fe31cd, + 0xb51d13aea4a488dd,0x6babab6398bdbe41, + 0xe264589a4dcdab14,0xc696963c7eed2dd1, + 0x8d7eb76070a08aec,0xfc1e1de5cf543ca2, + 0xb0de65388cc8ada8,0x3b25a55f43294bcb, + 0xdd15fe86affad912,0x49ef0eb713f39ebe, + 0x8a2dbf142dfcc7ab,0x6e3569326c784337, + 0xacb92ed9397bf996,0x49c2c37f07965404, + 0xd7e77a8f87daf7fb,0xdc33745ec97be906, + 0x86f0ac99b4e8dafd,0x69a028bb3ded71a3, + 0xa8acd7c0222311bc,0xc40832ea0d68ce0c, + 0xd2d80db02aabd62b,0xf50a3fa490c30190, + 0x83c7088e1aab65db,0x792667c6da79e0fa, + 0xa4b8cab1a1563f52,0x577001b891185938, + 0xcde6fd5e09abcf26,0xed4c0226b55e6f86, + 0x80b05e5ac60b6178,0x544f8158315b05b4, + 0xa0dc75f1778e39d6,0x696361ae3db1c721, + 0xc913936dd571c84c,0x3bc3a19cd1e38e9, + 0xfb5878494ace3a5f,0x4ab48a04065c723, + 0x9d174b2dcec0e47b,0x62eb0d64283f9c76, + 0xc45d1df942711d9a,0x3ba5d0bd324f8394, + 0xf5746577930d6500,0xca8f44ec7ee36479, + 0x9968bf6abbe85f20,0x7e998b13cf4e1ecb, + 0xbfc2ef456ae276e8,0x9e3fedd8c321a67e, + 0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e, + 0x95d04aee3b80ece5,0xbba1f1d158724a12, + 0xbb445da9ca61281f,0x2a8a6e45ae8edc97, + 0xea1575143cf97226,0xf52d09d71a3293bd, + 0x924d692ca61be758,0x593c2626705f9c56, + 0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c, + 0xe498f455c38b997a,0xb6dfb9c0f956447, + 0x8edf98b59a373fec,0x4724bd4189bd5eac, + 0xb2977ee300c50fe7,0x58edec91ec2cb657, + 0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed, + 0x8b865b215899f46c,0xbd79e0d20082ee74, + 0xae67f1e9aec07187,0xecd8590680a3aa11, + 0xda01ee641a708de9,0xe80e6f4820cc9495, + 0x884134fe908658b2,0x3109058d147fdcdd, + 0xaa51823e34a7eede,0xbd4b46f0599fd415, + 0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a, + 0x850fadc09923329e,0x3e2cf6bc604ddb0, + 0xa6539930bf6bff45,0x84db8346b786151c, + 0xcfe87f7cef46ff16,0xe612641865679a63, + 0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e, + 0xa26da3999aef7749,0xe3be5e330f38f09d, + 0xcb090c8001ab551c,0x5cadf5bfd3072cc5, + 0xfdcb4fa002162a63,0x73d9732fc7c8f7f6, + 0x9e9f11c4014dda7e,0x2867e7fddcdd9afa, + 0xc646d63501a1511d,0xb281e1fd541501b8, + 0xf7d88bc24209a565,0x1f225a7ca91a4226, + 0x9ae757596946075f,0x3375788de9b06958, + 0xc1a12d2fc3978937,0x52d6b1641c83ae, + 0xf209787bb47d6b84,0xc0678c5dbd23a49a, + 0x9745eb4d50ce6332,0xf840b7ba963646e0, + 0xbd176620a501fbff,0xb650e5a93bc3d898, + 0xec5d3fa8ce427aff,0xa3e51f138ab4cebe, + 0x93ba47c980e98cdf,0xc66f336c36b10137, + 0xb8a8d9bbe123f017,0xb80b0047445d4184, + 0xe6d3102ad96cec1d,0xa60dc059157491e5, + 0x9043ea1ac7e41392,0x87c89837ad68db2f, + 0xb454e4a179dd1877,0x29babe4598c311fb, + 0xe16a1dc9d8545e94,0xf4296dd6fef3d67a, + 0x8ce2529e2734bb1d,0x1899e4a65f58660c, + 0xb01ae745b101e9e4,0x5ec05dcff72e7f8f, + 0xdc21a1171d42645d,0x76707543f4fa1f73, + 0x899504ae72497eba,0x6a06494a791c53a8, + 0xabfa45da0edbde69,0x487db9d17636892, + 0xd6f8d7509292d603,0x45a9d2845d3c42b6, + 0x865b86925b9bc5c2,0xb8a2392ba45a9b2, + 0xa7f26836f282b732,0x8e6cac7768d7141e, + 0xd1ef0244af2364ff,0x3207d795430cd926, + 0x8335616aed761f1f,0x7f44e6bd49e807b8, + 0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6, + 0xcd036837130890a1,0x36dba887c37a8c0f, + 0x802221226be55a64,0xc2494954da2c9789, + 0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c, + 0xc83553c5c8965d3d,0x6f92829494e5acc7, + 0xfa42a8b73abbf48c,0xcb772339ba1f17f9, + 0x9c69a97284b578d7,0xff2a760414536efb, + 0xc38413cf25e2d70d,0xfef5138519684aba, + 0xf46518c2ef5b8cd1,0x7eb258665fc25d69, + 0x98bf2f79d5993802,0xef2f773ffbd97a61, + 0xbeeefb584aff8603,0xaafb550ffacfd8fa, + 0xeeaaba2e5dbf6784,0x95ba2a53f983cf38, + 0x952ab45cfa97a0b2,0xdd945a747bf26183, + 0xba756174393d88df,0x94f971119aeef9e4, + 0xe912b9d1478ceb17,0x7a37cd5601aab85d, + 0x91abb422ccb812ee,0xac62e055c10ab33a, + 0xb616a12b7fe617aa,0x577b986b314d6009, + 0xe39c49765fdf9d94,0xed5a7e85fda0b80b, + 0x8e41ade9fbebc27d,0x14588f13be847307, + 0xb1d219647ae6b31c,0x596eb2d8ae258fc8, + 0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb, + 0x8aec23d680043bee,0x25de7bb9480d5854, + 0xada72ccc20054ae9,0xaf561aa79a10ae6a, + 0xd910f7ff28069da4,0x1b2ba1518094da04, + 0x87aa9aff79042286,0x90fb44d2f05d0842, + 0xa99541bf57452b28,0x353a1607ac744a53, + 0xd3fa922f2d1675f2,0x42889b8997915ce8, + 0x847c9b5d7c2e09b7,0x69956135febada11, + 0xa59bc234db398c25,0x43fab9837e699095, + 0xcf02b2c21207ef2e,0x94f967e45e03f4bb, + 0x8161afb94b44f57d,0x1d1be0eebac278f5, + 0xa1ba1ba79e1632dc,0x6462d92a69731732, + 0xca28a291859bbf93,0x7d7b8f7503cfdcfe, + 0xfcb2cb35e702af78,0x5cda735244c3d43e, + 0x9defbf01b061adab,0x3a0888136afa64a7, + 0xc56baec21c7a1916,0x88aaa1845b8fdd0, + 0xf6c69a72a3989f5b,0x8aad549e57273d45, + 0x9a3c2087a63f6399,0x36ac54e2f678864b, + 0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd, + 0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5, + 0x969eb7c47859e743,0x9f644ae5a4b1b325, + 0xbc4665b596706114,0x873d5d9f0dde1fee, + 0xeb57ff22fc0c7959,0xa90cb506d155a7ea, + 0x9316ff75dd87cbd8,0x9a7f12442d588f2, + 0xb7dcbf5354e9bece,0xc11ed6d538aeb2f, + 0xe5d3ef282a242e81,0x8f1668c8a86da5fa, + 0x8fa475791a569d10,0xf96e017d694487bc, + 0xb38d92d760ec4455,0x37c981dcc395a9ac, + 0xe070f78d3927556a,0x85bbe253f47b1417, + 0x8c469ab843b89562,0x93956d7478ccec8e, + 0xaf58416654a6babb,0x387ac8d1970027b2, + 0xdb2e51bfe9d0696a,0x6997b05fcc0319e, + 0x88fcf317f22241e2,0x441fece3bdf81f03, + 0xab3c2fddeeaad25a,0xd527e81cad7626c3, + 0xd60b3bd56a5586f1,0x8a71e223d8d3b074, + 0x85c7056562757456,0xf6872d5667844e49, + 0xa738c6bebb12d16c,0xb428f8ac016561db, + 0xd106f86e69d785c7,0xe13336d701beba52, + 0x82a45b450226b39c,0xecc0024661173473, + 0xa34d721642b06084,0x27f002d7f95d0190, + 0xcc20ce9bd35c78a5,0x31ec038df7b441f4, + 0xff290242c83396ce,0x7e67047175a15271, + 0x9f79a169bd203e41,0xf0062c6e984d386, + 0xc75809c42c684dd1,0x52c07b78a3e60868, + 0xf92e0c3537826145,0xa7709a56ccdf8a82, + 0x9bbcc7a142b17ccb,0x88a66076400bb691, + 0xc2abf989935ddbfe,0x6acff893d00ea435, + 0xf356f7ebf83552fe,0x583f6b8c4124d43, + 0x98165af37b2153de,0xc3727a337a8b704a, + 0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c, + 0xeda2ee1c7064130c,0x1162def06f79df73, + 0x9485d4d1c63e8be7,0x8addcb5645ac2ba8, + 0xb9a74a0637ce2ee1,0x6d953e2bd7173692, + 0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437, + 0x910ab1d4db9914a0,0x1d9c9892400a22a2, + 0xb54d5e4a127f59c8,0x2503beb6d00cab4b, + 0xe2a0b5dc971f303a,0x2e44ae64840fd61d, + 0x8da471a9de737e24,0x5ceaecfed289e5d2, + 0xb10d8e1456105dad,0x7425a83e872c5f47, + 0xdd50f1996b947518,0xd12f124e28f77719, + 0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f, + 0xace73cbfdc0bfb7b,0x636cc64d1001550b, + 0xd8210befd30efa5a,0x3c47f7e05401aa4e, + 0x8714a775e3e95c78,0x65acfaec34810a71, + 0xa8d9d1535ce3b396,0x7f1839a741a14d0d, + 0xd31045a8341ca07c,0x1ede48111209a050, + 0x83ea2b892091e44d,0x934aed0aab460432, + 0xa4e4b66b68b65d60,0xf81da84d5617853f, + 0xce1de40642e3f4b9,0x36251260ab9d668e, + 0x80d2ae83e9ce78f3,0xc1d72b7c6b426019, + 0xa1075a24e4421730,0xb24cf65b8612f81f, + 0xc94930ae1d529cfc,0xdee033f26797b627, + 0xfb9b7cd9a4a7443c,0x169840ef017da3b1, + 0x9d412e0806e88aa5,0x8e1f289560ee864e, + 0xc491798a08a2ad4e,0xf1a6f2bab92a27e2, + 0xf5b5d7ec8acb58a2,0xae10af696774b1db, + 0x9991a6f3d6bf1765,0xacca6da1e0a8ef29, + 0xbff610b0cc6edd3f,0x17fd090a58d32af3, + 0xeff394dcff8a948e,0xddfc4b4cef07f5b0, + 0x95f83d0a1fb69cd9,0x4abdaf101564f98e, + 0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1, + 0xea53df5fd18d5513,0x84c86189216dc5ed, + 0x92746b9be2f8552c,0x32fd3cf5b4e49bb4, + 0xb7118682dbb66a77,0x3fbc8c33221dc2a1, + 0xe4d5e82392a40515,0xfabaf3feaa5334a, + 0x8f05b1163ba6832d,0x29cb4d87f2a7400e, + 0xb2c71d5bca9023f8,0x743e20e9ef511012, + 0xdf78e4b2bd342cf6,0x914da9246b255416, + 0x8bab8eefb6409c1a,0x1ad089b6c2f7548e, + 0xae9672aba3d0c320,0xa184ac2473b529b1, + 0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e, + 0x8865899617fb1871,0x7e2fa67c7a658892, + 0xaa7eebfb9df9de8d,0xddbb901b98feeab7, + 0xd51ea6fa85785631,0x552a74227f3ea565, + 0x8533285c936b35de,0xd53a88958f87275f, + 0xa67ff273b8460356,0x8a892abaf368f137, + 0xd01fef10a657842c,0x2d2b7569b0432d85, + 0x8213f56a67f6b29b,0x9c3b29620e29fc73, + 0xa298f2c501f45f42,0x8349f3ba91b47b8f, + 0xcb3f2f7642717713,0x241c70a936219a73, + 0xfe0efb53d30dd4d7,0xed238cd383aa0110, + 0x9ec95d1463e8a506,0xf4363804324a40aa, + 0xc67bb4597ce2ce48,0xb143c6053edcd0d5, + 0xf81aa16fdc1b81da,0xdd94b7868e94050a, + 0x9b10a4e5e9913128,0xca7cf2b4191c8326, + 0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0, + 0xf24a01a73cf2dccf,0xbc633b39673c8cec, + 0x976e41088617ca01,0xd5be0503e085d813, + 0xbd49d14aa79dbc82,0x4b2d8644d8a74e18, + 0xec9c459d51852ba2,0xddf8e7d60ed1219e, + 0x93e1ab8252f33b45,0xcabb90e5c942b503, + 0xb8da1662e7b00a17,0x3d6a751f3b936243, + 0xe7109bfba19c0c9d,0xcc512670a783ad4, + 0x906a617d450187e2,0x27fb2b80668b24c5, + 0xb484f9dc9641e9da,0xb1f9f660802dedf6, + 0xe1a63853bbd26451,0x5e7873f8a0396973, + 0x8d07e33455637eb2,0xdb0b487b6423e1e8, + 0xb049dc016abc5e5f,0x91ce1a9a3d2cda62, + 0xdc5c5301c56b75f7,0x7641a140cc7810fb, + 0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d, + 0xac2820d9623bf429,0x546345fa9fbdcd44, + 0xd732290fbacaf133,0xa97c177947ad4095, + 0x867f59a9d4bed6c0,0x49ed8eabcccc485d, + 0xa81f301449ee8c70,0x5c68f256bfff5a74, + 0xd226fc195c6a2f8c,0x73832eec6fff3111, + 0x83585d8fd9c25db7,0xc831fd53c5ff7eab, + 0xa42e74f3d032f525,0xba3e7ca8b77f5e55, + 0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb, + 0x80444b5e7aa7cf85,0x7980d163cf5b81b3, + 0xa0555e361951c366,0xd7e105bcc332621f, + 0xc86ab5c39fa63440,0x8dd9472bf3fefaa7, + 0xfa856334878fc150,0xb14f98f6f0feb951, + 0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3, + 0xc3b8358109e84f07,0xa862f80ec4700c8, + 0xf4a642e14c6262c8,0xcd27bb612758c0fa, + 0x98e7e9cccfbd7dbd,0x8038d51cb897789c, + 0xbf21e44003acdd2c,0xe0470a63e6bd56c3, + 0xeeea5d5004981478,0x1858ccfce06cac74, + 0x95527a5202df0ccb,0xf37801e0c43ebc8, + 0xbaa718e68396cffd,0xd30560258f54e6ba, + 0xe950df20247c83fd,0x47c6b82ef32a2069, + 0x91d28b7416cdd27e,0x4cdc331d57fa5441, + 0xb6472e511c81471d,0xe0133fe4adf8e952, + 0xe3d8f9e563a198e5,0x58180fddd97723a6, + 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,}; +using powers = powers_template<>; + +} + +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/float_common.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/float_common.h new file mode 100644 index 000000000..c8e7e4fc9 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/float_common.h @@ -0,0 +1,362 @@ +#ifndef FASTFLOAT_FLOAT_COMMON_H +#define FASTFLOAT_FLOAT_COMMON_H + +#include <cfloat> +#include <cstdint> +#include <cassert> +#include <cstring> + +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ + || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ + || defined(__MINGW64__) \ + || defined(__s390x__) \ + || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \ + || defined(__EMSCRIPTEN__)) +#define FASTFLOAT_64BIT +#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ + || defined(__arm__) || defined(_M_ARM) \ + || defined(__MINGW32__)) +#define FASTFLOAT_32BIT +#else + // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. + // We can never tell the register width, but the SIZE_MAX is a good approximation. + // UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability. + #if SIZE_MAX == 0xffff + #error Unknown platform (16-bit, unsupported) + #elif SIZE_MAX == 0xffffffff + #define FASTFLOAT_32BIT + #elif SIZE_MAX == 0xffffffffffffffff + #define FASTFLOAT_64BIT + #else + #error Unknown platform (not 32-bit, not 64-bit?) + #endif +#endif + +#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) +#include <intrin.h> +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define FASTFLOAT_VISUAL_STUDIO 1 +#endif + +#ifdef _WIN32 +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || defined(__FreeBSD__) +#include <machine/endian.h> +#elif defined(sun) || defined(__sun) +#include <sys/byteorder.h> +#else +#include <endian.h> +#endif +# +#ifndef __BYTE_ORDER__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#ifndef __ORDER_LITTLE_ENDIAN__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#define FASTFLOAT_IS_BIG_ENDIAN 1 +#endif +#endif + +#ifdef FASTFLOAT_VISUAL_STUDIO +#define fastfloat_really_inline __forceinline +#else +#define fastfloat_really_inline inline __attribute__((always_inline)) +#endif + +#ifndef FASTFLOAT_ASSERT +#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); } +#endif + +#ifndef FASTFLOAT_DEBUG_ASSERT +#include <cassert> +#define FASTFLOAT_DEBUG_ASSERT(x) assert(x) +#endif + +// rust style `try!()` macro, or `?` operator +#define FASTFLOAT_TRY(x) { if (!(x)) return false; } + +namespace fast_float { + +// Compares two ASCII strings in a case insensitive manner. +inline bool fastfloat_strncasecmp(const char *input1, const char *input2, + size_t length) { + char running_diff{0}; + for (size_t i = 0; i < length; i++) { + running_diff |= (input1[i] ^ input2[i]); + } + return (running_diff == 0) || (running_diff == 32); +} + +#ifndef FLT_EVAL_METHOD +#error "FLT_EVAL_METHOD should be defined, please include cfloat." +#endif + +// a pointer and a length to a contiguous block of memory +template <typename T> +struct span { + const T* ptr; + size_t length; + span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {} + span() : ptr(nullptr), length(0) {} + + constexpr size_t len() const noexcept { + return length; + } + + const T& operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return ptr[index]; + } +}; + +struct value128 { + uint64_t low; + uint64_t high; + value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} + value128() : low(0), high(0) {} +}; + +/* result might be undefined when input_num is zero */ +fastfloat_really_inline int leading_zeroes(uint64_t input_num) { + assert(input_num > 0); +#ifdef FASTFLOAT_VISUAL_STUDIO + #if defined(_M_X64) || defined(_M_ARM64) + unsigned long leading_zero = 0; + // Search the mask data from most significant bit (MSB) + // to least significant bit (LSB) for a set bit (1). + _BitScanReverse64(&leading_zero, input_num); + return (int)(63 - leading_zero); + #else + int last_bit = 0; + if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32; + if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16; + if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8; + if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4; + if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2; + if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1; + return 63 - last_bit; + #endif +#else + return __builtin_clzll(input_num); +#endif +} + +#ifdef FASTFLOAT_32BIT + +// slow emulation routine for 32-bit +fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} + +// slow emulation routine for 32-bit +#if !defined(__MINGW64__) +fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, + uint64_t *hi) { + uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif // !__MINGW64__ + +#endif // FASTFLOAT_32BIT + + +// compute 64-bit a*b +fastfloat_really_inline value128 full_multiplication(uint64_t a, + uint64_t b) { + value128 answer; +#ifdef _M_ARM64 + // ARM64 has native support for 64-bit multiplications, no need to emulate + answer.high = __umulh(a, b); + answer.low = a * b; +#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__)) + answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 +#elif defined(FASTFLOAT_64BIT) + __uint128_t r = ((__uint128_t)a) * b; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); +#else + #error Not implemented +#endif + return answer; +} + +struct adjusted_mantissa { + uint64_t mantissa{0}; + int32_t power2{0}; // a negative value indicates an invalid result + adjusted_mantissa() = default; + bool operator==(const adjusted_mantissa &o) const { + return mantissa == o.mantissa && power2 == o.power2; + } + bool operator!=(const adjusted_mantissa &o) const { + return mantissa != o.mantissa || power2 != o.power2; + } +}; + +// Bias so we can get the real exponent with an invalid adjusted_mantissa. +constexpr static int32_t invalid_am_bias = -0x8000; + +constexpr static double powers_of_ten_double[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, + 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; +constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, + 1e6, 1e7, 1e8, 1e9, 1e10}; + +template <typename T> struct binary_format { + static inline constexpr int mantissa_explicit_bits(); + static inline constexpr int minimum_exponent(); + static inline constexpr int infinite_power(); + static inline constexpr int sign_index(); + static inline constexpr int min_exponent_fast_path(); + static inline constexpr int max_exponent_fast_path(); + static inline constexpr int max_exponent_round_to_even(); + static inline constexpr int min_exponent_round_to_even(); + static inline constexpr uint64_t max_mantissa_fast_path(); + static inline constexpr int largest_power_of_ten(); + static inline constexpr int smallest_power_of_ten(); + static inline constexpr T exact_power_of_ten(int64_t power); + static inline constexpr size_t max_digits(); +}; + +template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() { + return 52; +} +template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() { + return 23; +} + +template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() { + return 23; +} + +template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() { + return 10; +} + +template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() { + return -4; +} + +template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() { + return -17; +} + +template <> inline constexpr int binary_format<double>::minimum_exponent() { + return -1023; +} +template <> inline constexpr int binary_format<float>::minimum_exponent() { + return -127; +} + +template <> inline constexpr int binary_format<double>::infinite_power() { + return 0x7FF; +} +template <> inline constexpr int binary_format<float>::infinite_power() { + return 0xFF; +} + +template <> inline constexpr int binary_format<double>::sign_index() { return 63; } +template <> inline constexpr int binary_format<float>::sign_index() { return 31; } + +template <> inline constexpr int binary_format<double>::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -22; +#endif +} +template <> inline constexpr int binary_format<float>::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -10; +#endif +} + +template <> inline constexpr int binary_format<double>::max_exponent_fast_path() { + return 22; +} +template <> inline constexpr int binary_format<float>::max_exponent_fast_path() { + return 10; +} + +template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} +template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} + +template <> +inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) { + return powers_of_ten_double[power]; +} +template <> +inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) { + + return powers_of_ten_float[power]; +} + + +template <> +inline constexpr int binary_format<double>::largest_power_of_ten() { + return 308; +} +template <> +inline constexpr int binary_format<float>::largest_power_of_ten() { + return 38; +} + +template <> +inline constexpr int binary_format<double>::smallest_power_of_ten() { + return -342; +} +template <> +inline constexpr int binary_format<float>::smallest_power_of_ten() { + return -65; +} + +template <> inline constexpr size_t binary_format<double>::max_digits() { + return 769; +} +template <> inline constexpr size_t binary_format<float>::max_digits() { + return 114; +} + +template<typename T> +fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { + uint64_t word = am.mantissa; + word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits(); + word = negative + ? word | (uint64_t(1) << binary_format<T>::sign_index()) : word; +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + if (std::is_same<T, float>::value) { + ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian + } else { + ::memcpy(&value, &word, sizeof(T)); + } +#else + // For little-endian systems: + ::memcpy(&value, &word, sizeof(T)); +#endif +} + +} // namespace fast_float + +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/parse_number.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/parse_number.h new file mode 100644 index 000000000..62ae3b039 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/parse_number.h @@ -0,0 +1,113 @@ +#ifndef FASTFLOAT_PARSE_NUMBER_H +#define FASTFLOAT_PARSE_NUMBER_H + +#include "ascii_number.h" +#include "decimal_to_binary.h" +#include "digit_comparison.h" + +#include <cmath> +#include <cstring> +#include <limits> +#include <system_error> + +namespace fast_float { + + +namespace detail { +/** + * Special case +inf, -inf, nan, infinity, -infinity. + * The case comparisons could be made much faster given that we know that the + * strings a null-free and fixed. + **/ +template <typename T> +from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept { + from_chars_result answer; + answer.ptr = first; + answer.ec = std::errc(); // be optimistic + bool minusSign = false; + if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here + minusSign = true; + ++first; + } + if (last - first >= 3) { + if (fastfloat_strncasecmp(first, "nan", 3)) { + answer.ptr = (first += 3); + value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN(); + // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). + if(first != last && *first == '(') { + for(const char* ptr = first + 1; ptr != last; ++ptr) { + if (*ptr == ')') { + answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) + break; + } + else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_')) + break; // forbidden char, not nan(n-char-seq-opt) + } + } + return answer; + } + if (fastfloat_strncasecmp(first, "inf", 3)) { + if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) { + answer.ptr = first + 8; + } else { + answer.ptr = first + 3; + } + value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity(); + return answer; + } + } + answer.ec = std::errc::invalid_argument; + return answer; +} + +} // namespace detail + +template<typename T> +from_chars_result from_chars(const char *first, const char *last, + T &value, chars_format fmt /*= chars_format::general*/) noexcept { + return from_chars_advanced(first, last, value, parse_options{fmt}); +} + +template<typename T> +from_chars_result from_chars_advanced(const char *first, const char *last, + T &value, parse_options options) noexcept { + + static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported"); + + + from_chars_result answer; + if (first == last) { + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + return answer; + } + parsed_number_string pns = parse_number_string(first, last, options); + if (!pns.valid) { + return detail::parse_infnan(first, last, value); + } + answer.ec = std::errc(); // be optimistic + answer.ptr = pns.lastmatch; + // Next is Clinger's fast path. + if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) { + value = T(pns.mantissa); + if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); } + else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); } + if (pns.negative) { value = -value; } + return answer; + } + adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa); + if(pns.too_many_digits && am.power2 >= 0) { + if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) { + am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa); + } + } + // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0), + // then we need to go the long way around again. This is very uncommon. + if(am.power2 < 0) { am = digit_comp<T>(pns, am); } + to_float(pns.negative, am, value); + return answer; +} + +} // namespace fast_float + +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/simple_decimal_conversion.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/simple_decimal_conversion.h new file mode 100644 index 000000000..e87801480 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/simple_decimal_conversion.h @@ -0,0 +1,360 @@ +#ifndef FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H +#define FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H + +/** + * This code is meant to handle the case where we have more than 19 digits. + * + * It is based on work by Nigel Tao (at https://github.com/google/wuffs/) + * who credits Ken Thompson for the design (via a reference to the Go source + * code). + * + * Rob Pike suggested that this algorithm be called "Simple Decimal Conversion". + * + * It is probably not very fast but it is a fallback that should almost never + * be used in real life. Though it is not fast, it is "easily" understood and debugged. + **/ +#include "ascii_number.h" +#include "decimal_to_binary.h" +#include <cstdint> + +namespace fast_float { + +namespace detail { + +// remove all final zeroes +inline void trim(decimal &h) { + while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) { + h.num_digits--; + } +} + + + +inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t shift) { + shift &= 63; + constexpr uint16_t number_of_digits_decimal_left_shift_table[65] = { + 0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, + 0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, + 0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, + 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0, + 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA, + 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, + 0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, + 0x051C, 0x051C, + }; + uint32_t x_a = number_of_digits_decimal_left_shift_table[shift]; + uint32_t x_b = number_of_digits_decimal_left_shift_table[shift + 1]; + uint32_t num_new_digits = x_a >> 11; + uint32_t pow5_a = 0x7FF & x_a; + uint32_t pow5_b = 0x7FF & x_b; + constexpr uint8_t + number_of_digits_decimal_left_shift_table_powers_of_5[0x051C] = { + 5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, + 9, 0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, + 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, + 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, 5, 2, 5, 8, + 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6, + 9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, + 3, 6, 7, 4, 3, 1, 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, + 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, + 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6, 4, 4, 7, 7, 5, 3, + 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, + 4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, + 0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, + 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6, 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, + 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5, + 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2, + 5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, + 1, 1, 6, 4, 1, 5, 3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, + 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, + 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, 1, 3, 2, 8, 1, 2, + 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0, + 6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, + 0, 3, 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, + 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, + 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7, 2, + 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5, + 0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, + 3, 7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, + 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, + 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, 8, 6, 0, 8, 0, 8, + 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0, + 9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, + 5, 1, 4, 2, 1, 0, 8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, + 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, + 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7, + 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, + 9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, + 6, 7, 7, 8, 1, 0, 6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, + 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3, 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, + 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9, + 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4, + 9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, + 0, 6, 2, 5, 1, 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, + 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, + 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, 8, 3, 4, 0, 4, 5, + 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1, + 3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, + 3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9, + 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0, + 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, 7, 6, 2, + 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1, + 8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, + 1, 7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, + 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, + 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, 2, 4, 0, 6, 9, 5, + 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5, + }; + const uint8_t *pow5 = + &number_of_digits_decimal_left_shift_table_powers_of_5[pow5_a]; + uint32_t i = 0; + uint32_t n = pow5_b - pow5_a; + for (; i < n; i++) { + if (i >= h.num_digits) { + return num_new_digits - 1; + } else if (h.digits[i] == pow5[i]) { + continue; + } else if (h.digits[i] < pow5[i]) { + return num_new_digits - 1; + } else { + return num_new_digits; + } + } + return num_new_digits; +} + +inline uint64_t round(decimal &h) { + if ((h.num_digits == 0) || (h.decimal_point < 0)) { + return 0; + } else if (h.decimal_point > 18) { + return UINT64_MAX; + } + // at this point, we know that h.decimal_point >= 0 + uint32_t dp = uint32_t(h.decimal_point); + uint64_t n = 0; + for (uint32_t i = 0; i < dp; i++) { + n = (10 * n) + ((i < h.num_digits) ? h.digits[i] : 0); + } + bool round_up = false; + if (dp < h.num_digits) { + round_up = h.digits[dp] >= 5; // normally, we round up + // but we may need to round to even! + if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) { + round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1])); + } + } + if (round_up) { + n++; + } + return n; +} + +// computes h * 2^-shift +inline void decimal_left_shift(decimal &h, uint32_t shift) { + if (h.num_digits == 0) { + return; + } + uint32_t num_new_digits = number_of_digits_decimal_left_shift(h, shift); + int32_t read_index = int32_t(h.num_digits - 1); + uint32_t write_index = h.num_digits - 1 + num_new_digits; + uint64_t n = 0; + + while (read_index >= 0) { + n += uint64_t(h.digits[read_index]) << shift; + uint64_t quotient = n / 10; + uint64_t remainder = n - (10 * quotient); + if (write_index < max_digits) { + h.digits[write_index] = uint8_t(remainder); + } else if (remainder > 0) { + h.truncated = true; + } + n = quotient; + write_index--; + read_index--; + } + while (n > 0) { + uint64_t quotient = n / 10; + uint64_t remainder = n - (10 * quotient); + if (write_index < max_digits) { + h.digits[write_index] = uint8_t(remainder); + } else if (remainder > 0) { + h.truncated = true; + } + n = quotient; + write_index--; + } + h.num_digits += num_new_digits; + if (h.num_digits > max_digits) { + h.num_digits = max_digits; + } + h.decimal_point += int32_t(num_new_digits); + trim(h); +} + +// computes h * 2^shift +inline void decimal_right_shift(decimal &h, uint32_t shift) { + uint32_t read_index = 0; + uint32_t write_index = 0; + + uint64_t n = 0; + + while ((n >> shift) == 0) { + if (read_index < h.num_digits) { + n = (10 * n) + h.digits[read_index++]; + } else if (n == 0) { + return; + } else { + while ((n >> shift) == 0) { + n = 10 * n; + read_index++; + } + break; + } + } + h.decimal_point -= int32_t(read_index - 1); + if (h.decimal_point < -decimal_point_range) { // it is zero + h.num_digits = 0; + h.decimal_point = 0; + h.negative = false; + h.truncated = false; + return; + } + uint64_t mask = (uint64_t(1) << shift) - 1; + while (read_index < h.num_digits) { + uint8_t new_digit = uint8_t(n >> shift); + n = (10 * (n & mask)) + h.digits[read_index++]; + h.digits[write_index++] = new_digit; + } + while (n > 0) { + uint8_t new_digit = uint8_t(n >> shift); + n = 10 * (n & mask); + if (write_index < max_digits) { + h.digits[write_index++] = new_digit; + } else if (new_digit > 0) { + h.truncated = true; + } + } + h.num_digits = write_index; + trim(h); +} + +} // namespace detail + +template <typename binary> +adjusted_mantissa compute_float(decimal &d) { + adjusted_mantissa answer; + if (d.num_digits == 0) { + // should be zero + answer.power2 = 0; + answer.mantissa = 0; + return answer; + } + // At this point, going further, we can assume that d.num_digits > 0. + // + // We want to guard against excessive decimal point values because + // they can result in long running times. Indeed, we do + // shifts by at most 60 bits. We have that log(10**400)/log(2**60) ~= 22 + // which is fine, but log(10**299995)/log(2**60) ~= 16609 which is not + // fine (runs for a long time). + // + if(d.decimal_point < -324) { + // We have something smaller than 1e-324 which is always zero + // in binary64 and binary32. + // It should be zero. + answer.power2 = 0; + answer.mantissa = 0; + return answer; + } else if(d.decimal_point >= 310) { + // We have something at least as large as 0.1e310 which is + // always infinite. + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + constexpr uint32_t max_shift = 60; + constexpr uint32_t num_powers = 19; + constexpr uint8_t decimal_powers[19] = { + 0, 3, 6, 9, 13, 16, 19, 23, 26, 29, // + 33, 36, 39, 43, 46, 49, 53, 56, 59, // + }; + int32_t exp2 = 0; + while (d.decimal_point > 0) { + uint32_t n = uint32_t(d.decimal_point); + uint32_t shift = (n < num_powers) ? decimal_powers[n] : max_shift; + detail::decimal_right_shift(d, shift); + if (d.decimal_point < -decimal_point_range) { + // should be zero + answer.power2 = 0; + answer.mantissa = 0; + return answer; + } + exp2 += int32_t(shift); + } + // We shift left toward [1/2 ... 1]. + while (d.decimal_point <= 0) { + uint32_t shift; + if (d.decimal_point == 0) { + if (d.digits[0] >= 5) { + break; + } + shift = (d.digits[0] < 2) ? 2 : 1; + } else { + uint32_t n = uint32_t(-d.decimal_point); + shift = (n < num_powers) ? decimal_powers[n] : max_shift; + } + detail::decimal_left_shift(d, shift); + if (d.decimal_point > decimal_point_range) { + // we want to get infinity: + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + exp2 -= int32_t(shift); + } + // We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2]. + exp2--; + constexpr int32_t minimum_exponent = binary::minimum_exponent(); + while ((minimum_exponent + 1) > exp2) { + uint32_t n = uint32_t((minimum_exponent + 1) - exp2); + if (n > max_shift) { + n = max_shift; + } + detail::decimal_right_shift(d, n); + exp2 += int32_t(n); + } + if ((exp2 - minimum_exponent) >= binary::infinite_power()) { + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + + const int mantissa_size_in_bits = binary::mantissa_explicit_bits() + 1; + detail::decimal_left_shift(d, mantissa_size_in_bits); + + uint64_t mantissa = detail::round(d); + // It is possible that we have an overflow, in which case we need + // to shift back. + if(mantissa >= (uint64_t(1) << mantissa_size_in_bits)) { + detail::decimal_right_shift(d, 1); + exp2 += 1; + mantissa = detail::round(d); + if ((exp2 - minimum_exponent) >= binary::infinite_power()) { + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + } + answer.power2 = exp2 - binary::minimum_exponent(); + if(mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) { answer.power2--; } + answer.mantissa = mantissa & ((uint64_t(1) << binary::mantissa_explicit_bits()) - 1); + return answer; +} + +template <typename binary> +adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) { + decimal d = parse_decimal(first, last, options); + return compute_float<binary>(d); +} + +} // namespace fast_float +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/amalgamate.py b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/amalgamate.py new file mode 100644 index 000000000..b360d9988 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/amalgamate.py @@ -0,0 +1,56 @@ +# text parts +processed_files = { } + +# authors +for filename in ['AUTHORS', 'CONTRIBUTORS']: + with open(filename) as f: + text = '' + for line in f: + if filename == 'AUTHORS': + text += '// fast_float by ' + line + if filename == 'CONTRIBUTORS': + text += '// with contributions from ' + line + processed_files[filename] = text + +# licenses +for filename in ['LICENSE-MIT', 'LICENSE-APACHE']: + with open(filename) as f: + text = '' + for line in f: + text += '// ' + line + processed_files[filename] = text + +# code +for filename in [ 'fast_float.h', 'float_common.h', 'ascii_number.h', + 'fast_table.h', 'decimal_to_binary.h', 'bigint.h', + 'ascii_number.h', 'digit_comparison.h', 'parse_number.h']: + with open('include/fast_float/' + filename) as f: + text = '' + for line in f: + if line.startswith('#include "'): continue + text += line + processed_files[filename] = text + +# command line +import argparse + +parser = argparse.ArgumentParser(description='Amalgamate fast_float.') +parser.add_argument('--license', default='MIT', help='choose license') +parser.add_argument('--output', default='', help='output file (stdout if none') + +args = parser.parse_args() + +text = '\n\n'.join([ + processed_files['AUTHORS'], processed_files['CONTRIBUTORS'], + processed_files['LICENSE-' + args.license], + processed_files['fast_float.h'], processed_files['float_common.h'], + processed_files['ascii_number.h'], processed_files['fast_table.h'], + processed_files['decimal_to_binary.h'], processed_files['bigint.h'], + processed_files['ascii_number.h'], processed_files['digit_comparison.h'], + processed_files['parse_number.h']]) + +if args.output: + with open(args.output, 'wt') as f: + f.write(text) +else: + print(text) diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/analysis.py b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/analysis.py new file mode 100644 index 000000000..8dcbcd533 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/analysis.py @@ -0,0 +1,36 @@ +from math import floor + +def log2(x): + """returns ceil(log2(x)))""" + y = 0 + while((1<<y) < x): + y = y + 1 + return y + + +for q in range(1,17+1): + d = 5**q + b = 127 + log2(d) + t = 2** b + c = t//d + 1 + assert c < 2**128 + assert c >= 2**127 + K = 2**127 + if(not(c * K * d<=( K + 1) * t)): + print(q) + top = floor(t/(c * d - t)) + sys.exit(-1) + +for q in range(18, 344+1): + d = 5**q + b = 64 + 2*log2(d) + t = 2**b + c = t//d + 1 + assert c > 2**(64 +log2(d)) + K = 2**64 + if(not(c * K * d<=( K + 1) * t)): + print(q) + top = floor(t/(c * d - t)) + sys.exit(-1) + +print("all good")
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/table_generation.py b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/table_generation.py new file mode 100644 index 000000000..24fec7cdd --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/table_generation.py @@ -0,0 +1,31 @@ +def format(number): + upper = number // (1<<64) + lower = number % (1<<64) + print(""+hex(upper)+","+hex(lower)+",") + +for q in range(-342,0): + power5 = 5 ** -q + z = 0 + while( (1<<z) < power5) : + z += 1 + if(q >= -27): + b = z + 127 + c = 2 ** b // power5 + 1 + format(c) + else: + b = 2 * z + 2 * 64 + c = 2 ** b // power5 + 1 + # truncate + while(c >= (1<<128)): + c //= 2 + format(c) + +for q in range(0,308+1): + power5 = 5 ** q + # move the most significant bit in position + while(power5 < (1<<127)): + power5 *= 2 + # *truncate* + while(power5 >= (1<<128)): + power5 //= 2 + format(power5) diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/basictest.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/basictest.cpp new file mode 100644 index 000000000..1dd924e5b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/basictest.cpp @@ -0,0 +1,704 @@ +#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> + +#include "fast_float/fast_float.h" +#include <cmath> +#include <cstdio> +#include <iomanip> +#include <ios> +#include <limits> +#include <string> +#include <system_error> + +#ifndef SUPPLEMENTAL_TEST_DATA_DIR +#define SUPPLEMENTAL_TEST_DATA_DIR "data/" +#endif + +#ifndef __cplusplus +#error fastfloat requires a C++ compiler +#endif + +#ifndef FASTFLOAT_CPLUSPLUS +#if defined(_MSVC_LANG) && !defined(__clang__) +#define FASTFLOAT_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) +#else +#define FASTFLOAT_CPLUSPLUS __cplusplus +#endif +#endif + + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) +#define FASTFLOAT_ODDPLATFORM 1 +#endif +#if defined __has_include +#if __has_include (<filesystem>) +#else +// filesystem is not available +#define FASTFLOAT_ODDPLATFORM 1 +#endif +#else +// __has_include is not available +#define FASTFLOAT_ODDPLATFORM 1 +#endif + +// C++ 17 because it is otherwise annoying to browse all files in a directory. +// We also only run these tests on little endian systems. +#if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM) + +#include <iostream> +#include <filesystem> +#include <charconv> + +// return true on succcess +bool check_file(std::string file_name) { + std::cout << "Checking " << file_name << std::endl; + size_t number{0}; + std::fstream newfile(file_name, std::ios::in); + if (newfile.is_open()) { + std::string str; + while (std::getline(newfile, str)) { + if (str.size() > 0) { + // Read 32-bit hex + uint32_t float32; + auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(), + float32, 16); + if(r32.ec != std::errc()) { std::cerr << "32-bit parsing failure\n"; return false; } + // Read 64-bit hex + uint64_t float64; + auto r64 = std::from_chars(str.data() + 14, str.data() + str.size(), + float64, 16); + if(r64.ec != std::errc()) { std::cerr << "64-bit parsing failure\n"; return false; } + // The string to parse: + const char *number_string = str.data() + 31; + const char *end_of_string = str.data() + str.size(); + // Parse as 32-bit float + float parsed_32; + auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32); + if(fast_float_r32.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; } + // Parse as 64-bit float + double parsed_64; + auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64); + if(fast_float_r64.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; } + // Convert the floats to unsigned ints. + uint32_t float32_parsed; + uint64_t float64_parsed; + ::memcpy(&float32_parsed, &parsed_32, sizeof(parsed_32)); + ::memcpy(&float64_parsed, &parsed_64, sizeof(parsed_64)); + // Compare with expected results + if (float32_parsed != float32) { + std::cout << "bad 32 " << str << std::endl; + return false; + } + if (float64_parsed != float64) { + std::cout << "bad 64 " << str << std::endl; + return false; + } + number++; + } + } + std::cout << "checked " << std::defaultfloat << number << " values" << std::endl; + newfile.close(); // close the file object + } else { + std::cout << "Could not read " << file_name << std::endl; + return false; + } + return true; +} + + +TEST_CASE("supplemental") { + std::string path = SUPPLEMENTAL_TEST_DATA_DIR; + for (const auto & entry : std::filesystem::directory_iterator(path)) { + CHECK(check_file(entry.path().string())); + } +} +#endif + +TEST_CASE("leading_zeroes") { + constexpr const uint64_t bit = 1; + CHECK(fast_float::leading_zeroes(bit << 0) == 63); + CHECK(fast_float::leading_zeroes(bit << 1) == 62); + CHECK(fast_float::leading_zeroes(bit << 2) == 61); + CHECK(fast_float::leading_zeroes(bit << 61) == 2); + CHECK(fast_float::leading_zeroes(bit << 62) == 1); + CHECK(fast_float::leading_zeroes(bit << 63) == 0); +} + +#define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")" +#define fHexAndDec(v) std::hexfloat << (v) << " (" << std::defaultfloat << (v) << ")" + +void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) { + fast_float::value128 v; + v = fast_float::full_multiplication(lhs, rhs); + INFO("lhs=" << iHexAndDec(lhs) << " " << "rhs=" << iHexAndDec(rhs) + << "\n actualLo=" << iHexAndDec(v.low) << " " << "actualHi=" << iHexAndDec(v.high) + << "\n expectedLo=" << iHexAndDec(expected_lo) << " " << "expectedHi=" << iHexAndDec(expected_hi)); + CHECK_EQ(v.low, expected_lo); + CHECK_EQ(v.high, expected_hi); + v = fast_float::full_multiplication(rhs, lhs); + CHECK_EQ(v.low, expected_lo); + CHECK_EQ(v.high, expected_hi); +} + +TEST_CASE("full_multiplication") { + constexpr const uint64_t bit = 1; + // lhs rhs lo hi + test_full_multiplication(bit << 0 , bit << 0, 1u , 0u); + test_full_multiplication(bit << 0 , bit << 63, bit << 63, 0u); + test_full_multiplication(bit << 1 , bit << 63, 0u , 1u); + test_full_multiplication(bit << 63, bit << 0, bit << 63, 0u); + test_full_multiplication(bit << 63, bit << 1, 0u , 1u); + test_full_multiplication(bit << 63, bit << 2, 0u , 2u); + test_full_multiplication(bit << 63, bit << 63, 0u , bit << 62); +} + + +TEST_CASE("issue8") { + const char* s = + "3." + "141592653589793238462643383279502884197169399375105820974944592307816406" + "286208998628034825342117067982148086513282306647093844609550582231725359" + "408128481117450284102701938521105559644622948954930381964428810975665933" + "446128475648233786783165271201909145648566923460348610454326648213393607" + "260249141273724587006606315588174881520920962829254091715364367892590360" + "011330530548820466521384146951941511609433057270365759591953092186117381" + "932611793105118548074462379962749567351885752724891227938183011949129833" + "673362440656643086021394946395224737190702179860943702770539217176293176" + "752384674818467669405132000568127145263560827785771342757789609173637178" + "721468440901224953430146549585371050792279689258923542019956112129021960" + "864034418159813629774771309960518707211349999998372978"; + for (int i = 0; i < 16; i++) { + // Parse all but the last i chars. We should still get 3.141ish. + double d = 0.0; + auto answer = fast_float::from_chars(s, s + strlen(s) - i, d); + CHECK_MESSAGE(answer.ec == std::errc(), "i=" << i); + CHECK_MESSAGE(d == 0x1.921fb54442d18p+1, "i=" << i << "\n" + << std::string(s, strlen(s) - size_t(i)) << "\n" + << std::hexfloat << d << "\n" + << std::defaultfloat << "\n"); + } +} + +TEST_CASE("check_behavior") { + const std::string input = "abc"; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); + CHECK_MESSAGE(answer.ec != std::errc(), "expected parse failure"); + CHECK_MESSAGE(answer.ptr == input.data(), "If there is no pattern match, we should have ptr equals first"); +} + + +TEST_CASE("decimal_point_parsing") { + double result; + fast_float::parse_options options{}; + { + const std::string input = "1,25"; + auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options); + CHECK_MESSAGE(answer.ec == std::errc(), "expected parse success"); + CHECK_MESSAGE(answer.ptr == input.data() + 1, + "Parsing should have stopped at comma"); + CHECK_EQ(result, 1.0); + + options.decimal_point = ','; + answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options); + CHECK_MESSAGE(answer.ec == std::errc(), "expected parse success"); + CHECK_MESSAGE(answer.ptr == input.data() + input.size(), + "Parsing should have stopped at end"); + CHECK_EQ(result, 1.25); + } + { + const std::string input = "1.25"; + auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options); + CHECK_MESSAGE(answer.ec == std::errc(), "expected parse success"); + CHECK_MESSAGE(answer.ptr == input.data() + 1, + "Parsing should have stopped at dot"); + CHECK_EQ(result, 1.0); + + options.decimal_point = '.'; + answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options); + CHECK_MESSAGE(answer.ec == std::errc(), "expected parse success"); + CHECK_MESSAGE(answer.ptr == input.data() + input.size(), + "Parsing should have stopped at end"); + CHECK_EQ(result, 1.25); + } +} + +TEST_CASE("issue19") { + const std::string input = "3.14e"; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); + CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 3.14\n"); + CHECK_MESSAGE(answer.ptr == input.data() + 4, + "Parsed the number " << result + << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); +} + + +TEST_CASE("scientific_only") { + // first, we try with something that should fail... + std::string input = "3.14"; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result, fast_float::chars_format::scientific); + CHECK_MESSAGE(answer.ec != std::errc(), "It is not scientific! Parsed: " << result); + + input = "3.14e10"; + answer = fast_float::from_chars(input.data(), input.data()+input.size(), result, fast_float::chars_format::scientific); + CHECK_MESSAGE(answer.ec == std::errc(), "It is scientific! Parsed: " << result); + CHECK_MESSAGE(answer.ptr == input.data() + input.size(), + "Parsed the number " << result + << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); +} + + +TEST_CASE("test_fixed_only") { + const std::string input = "3.14e10"; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result, fast_float::chars_format::fixed); + CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 3.14; parsed: " << result); + CHECK_MESSAGE(answer.ptr == input.data() + 4, + "Parsed the number " << result + << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); +} + + +static const double testing_power_of_ten[] = { + 1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299, + 1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, + 1e-289, 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, + 1e-280, 1e-279, 1e-278, 1e-277, 1e-276, 1e-275, 1e-274, 1e-273, 1e-272, + 1e-271, 1e-270, 1e-269, 1e-268, 1e-267, 1e-266, 1e-265, 1e-264, 1e-263, + 1e-262, 1e-261, 1e-260, 1e-259, 1e-258, 1e-257, 1e-256, 1e-255, 1e-254, + 1e-253, 1e-252, 1e-251, 1e-250, 1e-249, 1e-248, 1e-247, 1e-246, 1e-245, + 1e-244, 1e-243, 1e-242, 1e-241, 1e-240, 1e-239, 1e-238, 1e-237, 1e-236, + 1e-235, 1e-234, 1e-233, 1e-232, 1e-231, 1e-230, 1e-229, 1e-228, 1e-227, + 1e-226, 1e-225, 1e-224, 1e-223, 1e-222, 1e-221, 1e-220, 1e-219, 1e-218, + 1e-217, 1e-216, 1e-215, 1e-214, 1e-213, 1e-212, 1e-211, 1e-210, 1e-209, + 1e-208, 1e-207, 1e-206, 1e-205, 1e-204, 1e-203, 1e-202, 1e-201, 1e-200, + 1e-199, 1e-198, 1e-197, 1e-196, 1e-195, 1e-194, 1e-193, 1e-192, 1e-191, + 1e-190, 1e-189, 1e-188, 1e-187, 1e-186, 1e-185, 1e-184, 1e-183, 1e-182, + 1e-181, 1e-180, 1e-179, 1e-178, 1e-177, 1e-176, 1e-175, 1e-174, 1e-173, + 1e-172, 1e-171, 1e-170, 1e-169, 1e-168, 1e-167, 1e-166, 1e-165, 1e-164, + 1e-163, 1e-162, 1e-161, 1e-160, 1e-159, 1e-158, 1e-157, 1e-156, 1e-155, + 1e-154, 1e-153, 1e-152, 1e-151, 1e-150, 1e-149, 1e-148, 1e-147, 1e-146, + 1e-145, 1e-144, 1e-143, 1e-142, 1e-141, 1e-140, 1e-139, 1e-138, 1e-137, + 1e-136, 1e-135, 1e-134, 1e-133, 1e-132, 1e-131, 1e-130, 1e-129, 1e-128, + 1e-127, 1e-126, 1e-125, 1e-124, 1e-123, 1e-122, 1e-121, 1e-120, 1e-119, + 1e-118, 1e-117, 1e-116, 1e-115, 1e-114, 1e-113, 1e-112, 1e-111, 1e-110, + 1e-109, 1e-108, 1e-107, 1e-106, 1e-105, 1e-104, 1e-103, 1e-102, 1e-101, + 1e-100, 1e-99, 1e-98, 1e-97, 1e-96, 1e-95, 1e-94, 1e-93, 1e-92, + 1e-91, 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84, 1e-83, + 1e-82, 1e-81, 1e-80, 1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74, + 1e-73, 1e-72, 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65, + 1e-64, 1e-63, 1e-62, 1e-61, 1e-60, 1e-59, 1e-58, 1e-57, 1e-56, + 1e-55, 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, 1e-47, + 1e-46, 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40, 1e-39, 1e-38, + 1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29, + 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20, + 1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, + 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, + 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, + 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, + 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, + 1e26, 1e27, 1e28, 1e29, 1e30, 1e31, 1e32, 1e33, 1e34, + 1e35, 1e36, 1e37, 1e38, 1e39, 1e40, 1e41, 1e42, 1e43, + 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, 1e50, 1e51, 1e52, + 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 1e60, 1e61, + 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 1e70, + 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, + 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, + 1e89, 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, + 1e98, 1e99, 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, + 1e107, 1e108, 1e109, 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, + 1e116, 1e117, 1e118, 1e119, 1e120, 1e121, 1e122, 1e123, 1e124, + 1e125, 1e126, 1e127, 1e128, 1e129, 1e130, 1e131, 1e132, 1e133, + 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 1e140, 1e141, 1e142, + 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 1e150, 1e151, + 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 1e160, + 1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, 1e169, + 1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, + 1e179, 1e180, 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, + 1e188, 1e189, 1e190, 1e191, 1e192, 1e193, 1e194, 1e195, 1e196, + 1e197, 1e198, 1e199, 1e200, 1e201, 1e202, 1e203, 1e204, 1e205, + 1e206, 1e207, 1e208, 1e209, 1e210, 1e211, 1e212, 1e213, 1e214, + 1e215, 1e216, 1e217, 1e218, 1e219, 1e220, 1e221, 1e222, 1e223, + 1e224, 1e225, 1e226, 1e227, 1e228, 1e229, 1e230, 1e231, 1e232, + 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239, 1e240, 1e241, + 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249, 1e250, + 1e251, 1e252, 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259, + 1e260, 1e261, 1e262, 1e263, 1e264, 1e265, 1e266, 1e267, 1e268, + 1e269, 1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, 1e277, + 1e278, 1e279, 1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286, + 1e287, 1e288, 1e289, 1e290, 1e291, 1e292, 1e293, 1e294, 1e295, + 1e296, 1e297, 1e298, 1e299, 1e300, 1e301, 1e302, 1e303, 1e304, + 1e305, 1e306, 1e307, 1e308}; + + +TEST_CASE("powers_of_ten") { + char buf[1024]; + WARN_MESSAGE(1e-308 == std::pow(10, -308), "On your system, the pow function is busted. Sorry about that."); + bool is_pow_correct{1e-308 == std::pow(10,-308)}; + // large negative values should be zero. + int start_point = is_pow_correct ? -1000 : -307; + for (int i = start_point; i <= 308; ++i) { + INFO("i=" << i); + size_t n = size_t(snprintf(buf, sizeof(buf), "1e%d", i)); + REQUIRE(n < sizeof(buf)); // if false, fails the test and exits + double actual; + auto result = fast_float::from_chars(buf, buf + 1000, actual); + CHECK_MESSAGE(result.ec == std::errc(), " I could not parse " << buf); + double expected = ((i >= -307) ? testing_power_of_ten[i + 307] : std::pow(10, i)); + CHECK_MESSAGE(actual == expected, "String '" << buf << "'parsed to " << actual); + } +} + + +template <typename T> std::string to_string(T d) { + std::string s(64, '\0'); + auto written = std::snprintf(&s[0], s.size(), "%.*e", + std::numeric_limits<T>::max_digits10 - 1, d); + s.resize(size_t(written)); + return s; +} + +template <typename T> std::string to_long_string(T d) { + std::string s(4096, '\0'); + auto written = std::snprintf(&s[0], s.size(), "%.*e", + std::numeric_limits<T>::max_digits10 * 10, d); + s.resize(size_t(written)); + return s; +} + +uint32_t get_mantissa(float f) { + uint32_t m; + memcpy(&m, &f, sizeof(f)); + return (m & ((uint32_t(1)<<23)-1)); +} + +uint64_t get_mantissa(double f) { + uint64_t m; + memcpy(&m, &f, sizeof(f)); + return (m & ((uint64_t(1)<<57)-1)); +} + + +std::string append_zeros(std::string str, size_t number_of_zeros) { + std::string answer(str); + for(size_t i = 0; i < number_of_zeros; i++) { answer += "0"; } + return answer; +} + +template<class T> +void check_basic_test_result(const std::string& str, fast_float::from_chars_result result, + T actual, T expected) { + INFO("str=" << str << "\n" + << " expected=" << fHexAndDec(expected) << "\n" + << " ..actual=" << fHexAndDec(actual) << "\n" + << " expected mantissa=" << iHexAndDec(get_mantissa(expected)) << "\n" + << " ..actual mantissa=" << iHexAndDec(get_mantissa(actual))); + CHECK_EQ(result.ec, std::errc()); + CHECK_EQ(result.ptr, str.data() + str.size()); + CHECK_EQ(copysign(1, actual), copysign(1, expected)); + CHECK_EQ(std::isnan(actual), std::isnan(expected)); + CHECK_EQ(actual, expected); +} + +template<class T> +void basic_test(std::string str, T expected) { + T actual; + auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual); + check_basic_test_result(str, result, actual, expected); +} + +template<class T> +void basic_test(std::string str, T expected, fast_float::parse_options options) { + T actual; + auto result = fast_float::from_chars_advanced(str.data(), str.data() + str.size(), actual, options); + check_basic_test_result(str, result, actual, expected); +} + +void basic_test(float val) { + { + std::string long_vals = to_long_string(val); + INFO("long vals: " << long_vals); + basic_test<float>(long_vals, val); + } + { + std::string vals = to_string(val); + INFO("vals: " << vals); + basic_test<float>(vals, val); + } +} + +#define verify(lhs, rhs) { INFO(lhs); basic_test(lhs, rhs); } +#define verify32(val) { INFO(#val); basic_test(val); } + +#define verify_options(lhs, rhs) { INFO(lhs); basic_test(lhs, rhs, options); } + +TEST_CASE("64bit.inf") { + verify("INF", std::numeric_limits<double>::infinity()); + verify("-INF", -std::numeric_limits<double>::infinity()); + verify("INFINITY", std::numeric_limits<double>::infinity()); + verify("-INFINITY", -std::numeric_limits<double>::infinity()); + verify("infinity", std::numeric_limits<double>::infinity()); + verify("-infinity", -std::numeric_limits<double>::infinity()); + verify("inf", std::numeric_limits<double>::infinity()); + verify("-inf", -std::numeric_limits<double>::infinity()); + verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits<double>::infinity()); + verify("-2139879401095466344511101915470454744.9813888656856943E+272", -std::numeric_limits<double>::infinity()); + verify("1.8e308", std::numeric_limits<double>::infinity()); + verify("1.832312213213213232132132143451234453123412321321312e308", std::numeric_limits<double>::infinity()); + verify("2e30000000000000000", std::numeric_limits<double>::infinity()); + verify("2e3000", std::numeric_limits<double>::infinity()); + verify("1.9e308", std::numeric_limits<double>::infinity()); +} + +TEST_CASE("64bit.general") { + verify("-2.2222222222223e-322",-0x1.68p-1069); + verify("9007199254740993.0", 0x1p+53); + verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328); + verify(append_zeros("9007199254740993.0",1000), 0x1p+53); + verify("10000000000000000000", 0x1.158e460913dp+63); + verify("10000000000000000000000000000001000000000000", 0x1.cb2d6f618c879p+142); + verify("10000000000000000000000000000000000000000001", 0x1.cb2d6f618c879p+142); + verify("1.1920928955078125e-07", 1.1920928955078125e-07); + verify("9355950000000000000.00000000000000000000000000000000001844674407370955161600000184467440737095516161844674407370955161407370955161618446744073709551616000184467440737095516166000001844674407370955161618446744073709551614073709551616184467440737095516160001844674407370955161601844674407370955674451616184467440737095516140737095516161844674407370955161600018446744073709551616018446744073709551611616000184467440737095001844674407370955161600184467440737095516160018446744073709551168164467440737095516160001844073709551616018446744073709551616184467440737095516160001844674407536910751601611616000184467440737095001844674407370955161600184467440737095516160018446744073709551616184467440737095516160001844955161618446744073709551616000184467440753691075160018446744073709",0x1.03ae05e8fca1cp+63); + verify("-0",-0.0); + verify("2.22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375e-308", 0x1.0000000000002p-1022); + verify("1.0000000000000006661338147750939242541790008544921875",1.0000000000000007); + verify("1090544144181609348835077142190",0x1.b8779f2474dfbp+99); + verify("2.2250738585072013e-308", 2.2250738585072013e-308); + verify("-92666518056446206563E3", -92666518056446206563E3); + verify("-92666518056446206563E3", -92666518056446206563E3); + verify("-42823146028335318693e-128",-42823146028335318693e-128); + verify("90054602635948575728E72",90054602635948575728E72); + verify("1.00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309", 1.00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309); + verify("0e9999999999999999999999999999", 0.0); + verify("-2402844368454405395.2", -2402844368454405395.2); + verify("2402844368454405395.2", 2402844368454405395.2); + verify("7.0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); + verify("7.0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); + verify("-1.7339253062092163730578609458683877051596800000000000000000000000e+42", -1.7339253062092163730578609458683877051596800000000000000000000000e+42); + verify("-2.0972622234386619214559824785284023792871122537545728000000000000e+52", -2.0972622234386619214559824785284023792871122537545728000000000000e+52); + verify("-1.0001803374372191849407179462120053338028379051879898808320000000e+57", -1.0001803374372191849407179462120053338028379051879898808320000000e+57); + verify("-1.8607245283054342363818436991534856973992070520151142825984000000e+58", -1.8607245283054342363818436991534856973992070520151142825984000000e+58); + verify("-1.9189205311132686907264385602245237137907390376574976000000000000e+52", -1.9189205311132686907264385602245237137907390376574976000000000000e+52); + verify("-2.8184483231688951563253238886553506793085187889855201280000000000e+54", -2.8184483231688951563253238886553506793085187889855201280000000000e+54); + verify("-1.7664960224650106892054063261344555646357024359107788800000000000e+53", -1.7664960224650106892054063261344555646357024359107788800000000000e+53); + verify("-2.1470977154320536489471030463761883783915110400000000000000000000e+45", -2.1470977154320536489471030463761883783915110400000000000000000000e+45); + verify("-4.4900312744003159009338275160799498340862630046359789166919680000e+61", -4.4900312744003159009338275160799498340862630046359789166919680000e+61); + verify("1", 1.0); + verify("1.797693134862315700000000000000001e308", 1.7976931348623157e308); + verify("3e-324", 0x0.0000000000001p-1022); + verify("1.00000006e+09", 0x1.dcd651ep+29); + verify("4.9406564584124653e-324", 0x0.0000000000001p-1022); + verify("4.9406564584124654e-324", 0x0.0000000000001p-1022); + verify("2.2250738585072009e-308", 0x0.fffffffffffffp-1022); + verify("2.2250738585072014e-308", 0x1p-1022); + verify("1.7976931348623157e308", 0x1.fffffffffffffp+1023); + verify("1.7976931348623158e308", 0x1.fffffffffffffp+1023); + verify("4503599627370496.5", 4503599627370496.5); + verify("4503599627475352.5", 4503599627475352.5); + verify("4503599627475353.5", 4503599627475353.5); + verify("2251799813685248.25", 2251799813685248.25); + verify("1125899906842624.125", 1125899906842624.125); + verify("1125899906842901.875", 1125899906842901.875); + verify("2251799813685803.75", 2251799813685803.75); + verify("4503599627370497.5", 4503599627370497.5); + verify("45035996.273704995", 45035996.273704995); + verify("45035996.273704985", 45035996.273704985); + verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375); + verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375); + verify("1438456663141390273526118207642235581183227845246331231162636653790368152091394196930365828634687637948157940776599182791387527135353034738357134110310609455693900824193549772792016543182680519740580354365467985440183598701312257624545562331397018329928613196125590274187720073914818062530830316533158098624984118889298281371812288789537310599037529113415438738954894752124724983067241108764488346454376699018673078404751121414804937224240805993123816932326223683090770561597570457793932985826162604255884529134126396282202126526253389383421806727954588525596114379801269094096329805054803089299736996870951258573010877404407451953846698609198213926882692078557033228265259305481198526059813164469187586693257335779522020407645498684263339921905227556616698129967412891282231685504660671277927198290009824680186319750978665734576683784255802269708917361719466043175201158849097881370477111850171579869056016061666173029059588433776015644439705050377554277696143928278093453792803846252715966016733222646442382892123940052441346822429721593884378212558701004356924243030059517489346646577724622498919752597382095222500311124181823512251071356181769376577651390028297796156208815375089159128394945710515861334486267101797497111125909272505194792870889617179758703442608016143343262159998149700606597792535574457560429226974273443630323818747730771316763398572110874959981923732463076884528677392654150010269822239401993427482376513231389212353583573566376915572650916866553612366187378959554983566712767093372906030188976220169058025354973622211666504549316958271880975697143546564469806791358707318873075708383345004090151974068325838177531266954177406661392229801349994695941509935655355652985723782153570084089560139142231.738475042362596875449154552392299548947138162081694168675340677843807613129780449323363759027012972466987370921816813162658754726545121090545507240267000456594786540949605260722461937870630634874991729398208026467698131898691830012167897399682179601734569071423681e-733", std::numeric_limits<double>::infinity()); + verify("-2240084132271013504.131248280843119943687942846658579428", -0x1.f1660a65b00bfp+60); +} + +TEST_CASE("64bit.decimal_point") { + fast_float::parse_options options{}; + options.decimal_point = ','; + + // infinities + verify_options("1,8e308", std::numeric_limits<double>::infinity()); + verify_options("1,832312213213213232132132143451234453123412321321312e308", std::numeric_limits<double>::infinity()); + verify_options("2e30000000000000000", std::numeric_limits<double>::infinity()); + verify_options("2e3000", std::numeric_limits<double>::infinity()); + verify_options("1,9e308", std::numeric_limits<double>::infinity()); + + // finites + verify_options("-2,2222222222223e-322",-0x1.68p-1069); + verify_options("9007199254740993,0", 0x1p+53); + verify_options("860228122,6654514319E+90", 0x1.92bb20990715fp+328); + verify_options(append_zeros("9007199254740993,0",1000), 0x1p+53); + verify_options("1,1920928955078125e-07", 1.1920928955078125e-07); + verify_options("9355950000000000000,00000000000000000000000000000000001844674407370955161600000184467440737095516161844674407370955161407370955161618446744073709551616000184467440737095516166000001844674407370955161618446744073709551614073709551616184467440737095516160001844674407370955161601844674407370955674451616184467440737095516140737095516161844674407370955161600018446744073709551616018446744073709551611616000184467440737095001844674407370955161600184467440737095516160018446744073709551168164467440737095516160001844073709551616018446744073709551616184467440737095516160001844674407536910751601611616000184467440737095001844674407370955161600184467440737095516160018446744073709551616184467440737095516160001844955161618446744073709551616000184467440753691075160018446744073709",0x1.03ae05e8fca1cp+63); + verify_options("2,22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375e-308", 0x1.0000000000002p-1022); + verify_options("1,0000000000000006661338147750939242541790008544921875",1.0000000000000007); + verify_options("2,2250738585072013e-308", 2.2250738585072013e-308); + verify_options("1,00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309", 1.00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309); + verify_options("-2402844368454405395,2", -2402844368454405395.2); + verify_options("2402844368454405395,2", 2402844368454405395.2); + verify_options("7,0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); + verify_options("7,0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); + verify_options("-1,7339253062092163730578609458683877051596800000000000000000000000e+42", -1.7339253062092163730578609458683877051596800000000000000000000000e+42); + verify_options("-2,0972622234386619214559824785284023792871122537545728000000000000e+52", -2.0972622234386619214559824785284023792871122537545728000000000000e+52); + verify_options("-1,0001803374372191849407179462120053338028379051879898808320000000e+57", -1.0001803374372191849407179462120053338028379051879898808320000000e+57); + verify_options("-1,8607245283054342363818436991534856973992070520151142825984000000e+58", -1.8607245283054342363818436991534856973992070520151142825984000000e+58); + verify_options("-1,9189205311132686907264385602245237137907390376574976000000000000e+52", -1.9189205311132686907264385602245237137907390376574976000000000000e+52); + verify_options("-2,8184483231688951563253238886553506793085187889855201280000000000e+54", -2.8184483231688951563253238886553506793085187889855201280000000000e+54); + verify_options("-1,7664960224650106892054063261344555646357024359107788800000000000e+53", -1.7664960224650106892054063261344555646357024359107788800000000000e+53); + verify_options("-2,1470977154320536489471030463761883783915110400000000000000000000e+45", -2.1470977154320536489471030463761883783915110400000000000000000000e+45); + verify_options("-4,4900312744003159009338275160799498340862630046359789166919680000e+61", -4.4900312744003159009338275160799498340862630046359789166919680000e+61); + verify_options("1", 1.0); + verify_options("1,797693134862315700000000000000001e308", 1.7976931348623157e308); + verify_options("3e-324", 0x0.0000000000001p-1022); + verify_options("1,00000006e+09", 0x1.dcd651ep+29); + verify_options("4,9406564584124653e-324", 0x0.0000000000001p-1022); + verify_options("4,9406564584124654e-324", 0x0.0000000000001p-1022); + verify_options("2,2250738585072009e-308", 0x0.fffffffffffffp-1022); + verify_options("2,2250738585072014e-308", 0x1p-1022); + verify_options("1,7976931348623157e308", 0x1.fffffffffffffp+1023); + verify_options("1,7976931348623158e308", 0x1.fffffffffffffp+1023); + verify_options("4503599627370496,5", 4503599627370496.5); + verify_options("4503599627475352,5", 4503599627475352.5); + verify_options("4503599627475353,5", 4503599627475353.5); + verify_options("2251799813685248,25", 2251799813685248.25); + verify_options("1125899906842624,125", 1125899906842624.125); + verify_options("1125899906842901,875", 1125899906842901.875); + verify_options("2251799813685803,75", 2251799813685803.75); + verify_options("4503599627370497,5", 4503599627370497.5); + verify_options("45035996,273704995", 45035996.273704995); + verify_options("45035996,273704985", 45035996.273704985); + verify_options("0,000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375); + verify_options("0,000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375); +} + + +TEST_CASE("32bit.inf") { + verify("INF", std::numeric_limits<float>::infinity()); + verify("-INF", -std::numeric_limits<float>::infinity()); + verify("INFINITY", std::numeric_limits<float>::infinity()); + verify("-INFINITY", -std::numeric_limits<float>::infinity()); + verify("infinity", std::numeric_limits<float>::infinity()); + verify("-infinity", -std::numeric_limits<float>::infinity()); + verify("inf", std::numeric_limits<float>::infinity()); + verify("-inf", -std::numeric_limits<float>::infinity()); + verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits<float>::infinity()); + verify("2e3000", std::numeric_limits<float>::infinity()); + verify("3.5028234666e38", std::numeric_limits<float>::infinity()); +} + +TEST_CASE("32bit.general") { + verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f); + verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f); + verify(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f); + verify(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656), 0x1.2ced3p+0f); + verify(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000), 0x1.2ced3p+0f); + std::string test_string; + test_string = append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655) + std::string("e-38"); + verify(test_string, 0x1.fffff8p-127f); + test_string = append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656) + std::string("e-38"); + verify(test_string, 0x1.fffff8p-127f); + test_string = append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000) + std::string("e-38"); + verify(test_string, 0x1.fffff8p-127f); + verify32(1.00000006e+09f); + verify32(1.4012984643e-45f); + verify32(1.1754942107e-38f); + verify32(1.1754943508e-45f); + verify("-0", -0.0f); + verify("1090544144181609348835077142190", 0x1.b877ap+99f); + verify("1.1754943508e-38", 1.1754943508e-38f); + verify("30219.0830078125", 30219.0830078125f); + verify("16252921.5", 16252921.5f); + verify("5322519.25", 5322519.25f); + verify("3900245.875", 3900245.875f); + verify("1510988.3125", 1510988.3125f); + verify("782262.28125", 782262.28125f); + verify("328381.484375", 328381.484375f); + verify("156782.0703125", 156782.0703125f); + verify("85003.24609375", 85003.24609375f); + verify("17419.6494140625", 17419.6494140625f); + verify("15498.36376953125", 15498.36376953125f); + verify("6318.580322265625", 6318.580322265625f); + verify("2525.2840576171875", 2525.2840576171875f); + verify("1370.9265747070312", 1370.9265747070312f); + verify("936.3702087402344", 936.3702087402344f); + verify("411.88682556152344", 411.88682556152344f); + verify("206.50310516357422", 206.50310516357422f); + verify("124.16878890991211", 124.16878890991211f); + verify("50.811574935913086", 50.811574935913086f); + verify("17.486443519592285", 17.486443519592285f); + verify("13.91745138168335", 13.91745138168335f); + verify("7.5464513301849365", 0x1.e2f90ep+2f); + verify("2.687217116355896", 2.687217116355896f); + verify("1.1877630352973938", 0x1.30113ep+0f); + verify("0.7622503340244293", 0.7622503340244293f); + verify("0.30531780421733856", 0x1.38a53ap-2f); + verify("0.21791061013936996", 0x1.be47eap-3f); + verify("0.09289376810193062", 0x1.7c7e2ep-4f); + verify("0.03706067614257336", 0.03706067614257336f); + verify("0.028068351559340954", 0.028068351559340954f); + verify("0.012114629615098238", 0x1.8cf8e2p-7f); + verify("0.004221370676532388", 0x1.14a6dap-8f); + verify("0.002153817447833717", 0.002153817447833717f); + verify("0.0015924838953651488", 0x1.a175cap-10f); + verify("0.0008602388261351734", 0.0008602388261351734f); + verify("0.00036393293703440577", 0x1.7d9c82p-12f); + verify("0.00013746770127909258", 0.00013746770127909258f); + verify("16407.9462890625", 16407.9462890625f); + verify("1.1754947011469036e-38", 0x1.000006p-126f); + verify("7.0064923216240854e-46", 0x1p-149f); + verify("8388614.5", 8388614.5f); + verify("0e9999999999999999999999999999", 0.f); + verify("4.7019774032891500318749461488889827112746622270883500860350068251e-38",4.7019774032891500318749461488889827112746622270883500860350068251e-38f); + verify("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f); + verify("2.3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f); + verify("1", 1.f); + verify("7.0060e-46", 0.f); + verify("3.4028234664e38", 0x1.fffffep+127f); + verify("3.4028234665e38", 0x1.fffffep+127f); + verify("3.4028234666e38", 0x1.fffffep+127f); + verify("0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625", 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625); + verify("0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125", 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125f); + verify("0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875", 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875f); + verify("0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875", 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f); +} + +TEST_CASE("32bit.decimal_point") { + fast_float::parse_options options{}; + options.decimal_point = ','; + + // infinity + verify_options("3,5028234666e38", std::numeric_limits<float>::infinity()); + + // finites + verify_options("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f); + verify_options("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f); + verify_options(append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f); + verify_options(append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656), 0x1.2ced3p+0f); + verify_options(append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000), 0x1.2ced3p+0f); + std::string test_string; + test_string = append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655) + std::string("e-38"); + verify_options(test_string, 0x1.fffff8p-127f); + test_string = append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656) + std::string("e-38"); + verify_options(test_string, 0x1.fffff8p-127f); + test_string = append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000) + std::string("e-38"); + verify_options(test_string, 0x1.fffff8p-127f); + verify_options("1,1754943508e-38", 1.1754943508e-38f); + verify_options("30219,0830078125", 30219.0830078125f); + verify_options("1,1754947011469036e-38", 0x1.000006p-126f); + verify_options("7,0064923216240854e-46", 0x1p-149f); + verify_options("8388614,5", 8388614.5f); + verify_options("0e9999999999999999999999999999", 0.f); + verify_options("4,7019774032891500318749461488889827112746622270883500860350068251e-38",4.7019774032891500318749461488889827112746622270883500860350068251e-38f); + verify_options("3,1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f); + verify_options("2,3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f); + verify_options("1", 1.f); + verify_options("7,0060e-46", 0.f); + verify_options("3,4028234664e38", 0x1.fffffep+127f); + verify_options("3,4028234665e38", 0x1.fffffep+127f); + verify_options("3,4028234666e38", 0x1.fffffep+127f); + verify_options("0,000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625", 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625); + verify_options("0,00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125", 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125f); + verify_options("0,00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875", 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875f); + verify_options("0,00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875", 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f); +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/foo.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/foo.cpp new file mode 100644 index 000000000..cbe241275 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/foo.cpp @@ -0,0 +1,2 @@ +#include "test.h" +void foo() { }
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/main.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/main.cpp new file mode 100644 index 000000000..3d46c0abc --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/main.cpp @@ -0,0 +1,2 @@ +#include "test.h" +int main() { return 0; }
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/test.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/test.h new file mode 100644 index 000000000..550a31b5a --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/test.h @@ -0,0 +1,2 @@ +#pragma once +#include "fast_float/fast_float.h"
\ No newline at end of file diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/example_comma_test.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/example_comma_test.cpp new file mode 100644 index 000000000..12f488d1a --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/example_comma_test.cpp @@ -0,0 +1,15 @@ + +#include "fast_float/fast_float.h" +#include <iostream> +#include <string> +#include <system_error> + +int main() { + const std::string input = "3,1416 xyz "; + double result; + fast_float::parse_options options{fast_float::chars_format::general, ','}; + auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options); + if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } + std::cout << "parsed the number " << result << std::endl; + return EXIT_SUCCESS; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/example_test.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/example_test.cpp new file mode 100644 index 000000000..db1bae829 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/example_test.cpp @@ -0,0 +1,14 @@ + +#include "fast_float/fast_float.h" +#include <iostream> +#include <string> +#include <system_error> + +int main() { + const std::string input = "3.1416 xyz "; + double result; + auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); + if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } + std::cout << "parsed the number " << result << std::endl; + return EXIT_SUCCESS; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32.cpp new file mode 100644 index 000000000..9e1e42f26 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32.cpp @@ -0,0 +1,63 @@ + +#include "fast_float/fast_float.h" + +#include <cassert> +#include <cmath> +#include <cstdio> +#include <ios> +#include <iostream> +#include <limits> +#include <system_error> + +template <typename T> char *to_string(T d, char *buffer) { + auto written = std::snprintf(buffer, 64, "%.*e", + std::numeric_limits<T>::max_digits10 - 1, d); + return buffer + written; +} + +void allvalues() { + char buffer[64]; + for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { + float v; + if ((w % 1048576) == 0) { + std::cout << "."; + std::cout.flush(); + } + uint32_t word = uint32_t(w); + memcpy(&v, &word, sizeof(v)); + + { + const char *string_end = to_string(v, buffer); + float result_value; + auto result = fast_float::from_chars(buffer, string_end, result_value); + if (result.ec != std::errc()) { + std::cerr << "parsing error ? " << buffer << std::endl; + abort(); + } + if (std::isnan(v)) { + if (!std::isnan(result_value)) { + std::cerr << "not nan" << buffer << std::endl; + abort(); + } + } else if(copysign(1,result_value) != copysign(1,v)) { + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v + << std::endl; + abort(); + } else if (result_value != v) { + std::cerr << "no match ? " << buffer << std::endl; + std::cout << "started with " << std::hexfloat << v << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << std::dec; + abort(); + } + } + } + std::cout << std::endl; +} + +int main() { + allvalues(); + std::cout << std::endl; + std::cout << "all ok" << std::endl; + return EXIT_SUCCESS; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32_64.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32_64.cpp new file mode 100644 index 000000000..e02757bd3 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32_64.cpp @@ -0,0 +1,78 @@ + +#include "fast_float/fast_float.h" + +#include <cassert> +#include <cmath> +#include <cstdio> +#include <ios> +#include <iostream> +#include <limits> +#include <string> +#include <system_error> + +template <typename T> char *to_string(T d, char *buffer) { + auto written = std::snprintf(buffer, 64, "%.*e", + std::numeric_limits<T>::max_digits10 - 1, d); + return buffer + written; +} + + +bool basic_test_64bit(std::string vals, double val) { + double result_value; + auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(), + result_value); + if (result.ec != std::errc()) { + std::cerr << " I could not parse " << vals << std::endl; + return false; + } + if (std::isnan(val)) { + if (!std::isnan(result_value)) { + std::cerr << vals << std::endl; + std::cerr << "not nan" << result_value << std::endl; + return false; + } + } else if(copysign(1,result_value) != copysign(1,val)) { + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val + << std::endl; + return false; + } else if (result_value != val) { + std::cerr << vals << std::endl; + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val + << std::endl; + std::cerr << std::dec; + std::cerr << "string: " << vals << std::endl; + return false; + } + return true; +} + + +void all_32bit_values() { + char buffer[64]; + for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { + float v32; + if ((w % 1048576) == 0) { + std::cout << "."; + std::cout.flush(); + } + uint32_t word = uint32_t(w); + memcpy(&v32, &word, sizeof(v32)); + double v = v32; + + { + const char *string_end = to_string(v, buffer); + std::string s(buffer, size_t(string_end-buffer)); + if(!basic_test_64bit(s,v)) { + return; + } + } + } + std::cout << std::endl; +} + +int main() { + all_32bit_values(); + std::cout << std::endl; + std::cout << "all ok" << std::endl; + return EXIT_SUCCESS; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32_midpoint.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32_midpoint.cpp new file mode 100644 index 000000000..f64f4e481 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32_midpoint.cpp @@ -0,0 +1,148 @@ +#include "fast_float/fast_float.h" + +#include <cassert> +#include <cmath> +#include <cstdio> +#include <ios> +#include <iostream> +#include <limits> +#include <stdexcept> + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +// Anything at all that is related to cygwin, msys and so forth will +// always use this fallback because we cannot rely on it behaving as normal +// gcc. +#include <locale> +#include <sstream> +// workaround for CYGWIN +double cygwin_strtod_l(const char* start, char** end) { + double d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +float cygwin_strtof_l(const char* start, char** end) { + float d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +#endif + +template <typename T> char *to_string(T d, char *buffer) { + auto written = std::snprintf(buffer, 64, "%.*e", + std::numeric_limits<T>::max_digits10 - 1, d); + return buffer + written; +} + +void strtof_from_string(const char * st, float& d) { + char *pr = (char *)st; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + d = cygwin_strtof_l(st, &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtof_l(st, &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtof_l(st, &pr, c_locale); +#endif + if (pr == st) { + throw std::runtime_error("bug in strtod_from_string"); + } +} + +bool allvalues() { + char buffer[64]; + for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { + float v; + if ((w % 1048576) == 0) { + std::cout << "."; + std::cout.flush(); + } + uint32_t word = uint32_t(w); + memcpy(&v, &word, sizeof(v)); + if(std::isfinite(v)) { + float nextf = std::nextafterf(v, INFINITY); + if(copysign(1,v) != copysign(1,nextf)) { continue; } + if(!std::isfinite(nextf)) { continue; } + double v1{v}; + assert(float(v1) == v); + double v2{nextf}; + assert(float(v2) == nextf); + double midv{v1 + (v2 - v1) / 2}; + float expected_midv = float(midv); + + const char *string_end = to_string(midv, buffer); + float str_answer; + strtof_from_string(buffer, str_answer); + + float result_value; + auto result = fast_float::from_chars(buffer, string_end, result_value); + if (result.ec != std::errc()) { + std::cerr << "parsing error ? " << buffer << std::endl; + return false; + } + if (std::isnan(v)) { + if (!std::isnan(result_value)) { + std::cerr << "not nan" << buffer << std::endl; + std::cerr << "v " << std::hexfloat << v << std::endl; + std::cerr << "v2 " << std::hexfloat << v2 << std::endl; + std::cerr << "midv " << std::hexfloat << midv << std::endl; + std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl; + return false; + } + } else if(copysign(1,result_value) != copysign(1,v)) { + std::cerr << buffer << std::endl; + std::cerr << "v " << std::hexfloat << v << std::endl; + std::cerr << "v2 " << std::hexfloat << v2 << std::endl; + std::cerr << "midv " << std::hexfloat << midv << std::endl; + std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl; + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v + << std::endl; + return false; + } else if (result_value != str_answer) { + std::cerr << "no match ? " << buffer << std::endl; + std::cerr << "v " << std::hexfloat << v << std::endl; + std::cerr << "v2 " << std::hexfloat << v2 << std::endl; + std::cerr << "midv " << std::hexfloat << midv << std::endl; + std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl; + std::cout << "started with " << std::hexfloat << midv << std::endl; + std::cout << "round down to " << std::hexfloat << str_answer << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << std::dec; + return false; + } + } + } + std::cout << std::endl; + return true; +} + +inline void Assert(bool Assertion) { +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + if (!Assertion) { std::cerr << "Omitting hard falure on msys/cygwin/sun systems."; } +#else + if (!Assertion) { throw std::runtime_error("bug"); } +#endif +} +int main() { +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + std::cout << "Warning: msys/cygwin or solaris detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library as a gold standard." << std::endl; +#endif + Assert(allvalues()); + std::cout << std::endl; + std::cout << "all ok" << std::endl; + return EXIT_SUCCESS; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_exhaustive32.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_exhaustive32.cpp new file mode 100644 index 000000000..0a6b53d81 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_exhaustive32.cpp @@ -0,0 +1,63 @@ + +#include "fast_float/fast_float.h" + +#include <cassert> +#include <cmath> +#include <cstdio> +#include <ios> +#include <iostream> +#include <system_error> + +template <typename T> char *to_string(T d, char *buffer) { + auto written = std::snprintf(buffer, 128, "%.*e", + 64, d); + return buffer + written; +} + +void allvalues() { + char buffer[128]; + for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { + float v; + if ((w % 1048576) == 0) { + std::cout << "."; + std::cout.flush(); + } + uint32_t word = uint32_t(w); + memcpy(&v, &word, sizeof(v)); + + { + const char *string_end = to_string(v, buffer); + float result_value; + auto result = fast_float::from_chars(buffer, string_end, result_value); + if (result.ec != std::errc()) { + std::cerr << "parsing error ? " << buffer << std::endl; + abort(); + } + if (std::isnan(v)) { + if (!std::isnan(result_value)) { + std::cerr << "not nan" << buffer << std::endl; + abort(); + } + } else if(copysign(1,result_value) != copysign(1,v)) { + std::cerr << buffer << std::endl; + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v + << std::endl; + abort(); + } else if (result_value != v) { + std::cerr << "no match ? " << buffer << " got " << result_value << " expected " << v << std::endl; + std::cout << "started with " << std::hexfloat << v << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << std::dec; + abort(); + } + } + } + std::cout << std::endl; +} + +int main() { + allvalues(); + std::cout << std::endl; + std::cout << "all ok" << std::endl; + return EXIT_SUCCESS; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_exhaustive32_64.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_exhaustive32_64.cpp new file mode 100644 index 000000000..cea8497f9 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_exhaustive32_64.cpp @@ -0,0 +1,66 @@ +#include "fast_float/fast_float.h" + +#include <cassert> +#include <cmath> +#include <cstdio> +#include <ios> +#include <iostream> + +template <typename T> char *to_string(T d, char *buffer) { + auto written = std::snprintf(buffer, 128, "%.*e", + 64, d); + return buffer + written; +} + +void all_32bit_values() { + char buffer[128]; + for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { + float v32; + if ((w % 1048576) == 0) { + std::cout << "."; + std::cout.flush(); + } + uint32_t word = uint32_t(w); + memcpy(&v32, &word, sizeof(v32)); + double v = v32; + + { + const char *string_end = to_string(v, buffer); + double result_value; + auto result = fast_float::from_chars(buffer, string_end, result_value); + if (result.ec != std::errc()) { + std::cerr << "parsing error ? " << buffer << std::endl; + abort(); + } + if (std::isnan(v)) { + if (!std::isnan(result_value)) { + std::cerr << "not nan" << buffer << std::endl; + abort(); + } + } else if(copysign(1,result_value) != copysign(1,v)) { + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v + << std::endl; + abort(); + } else if (std::isnan(v)) { + if (!std::isnan(result_value)) { + std::cerr << "not nan" << buffer << std::endl; + abort(); + } + } else if (result_value != v) { + std::cerr << "no match ? " << buffer << std::endl; + std::cout << "started with " << std::hexfloat << v << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << std::dec; + abort(); + } + } + } + std::cout << std::endl; +} + +int main() { + all_32bit_values(); + std::cout << std::endl; + std::cout << "all ok" << std::endl; + return EXIT_SUCCESS; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_random64.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_random64.cpp new file mode 100644 index 000000000..a6680e82c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_random64.cpp @@ -0,0 +1,106 @@ +#include "fast_float/fast_float.h" + +#include <cassert> +#include <cmath> +#include <cstdio> +#include <ios> +#include <iostream> +#include <system_error> + +template <typename T> char *to_string(T d, char *buffer) { + auto written = std::snprintf(buffer, 128, "%.*e", + 64, d); + return buffer + written; +} + +static fast_float::value128 g_lehmer64_state; + +/** + * D. H. Lehmer, Mathematical methods in large-scale computing units. + * Proceedings of a Second Symposium on Large Scale Digital Calculating + * Machinery; + * Annals of the Computation Laboratory, Harvard Univ. 26 (1951), pp. 141-146. + * + * P L'Ecuyer, Tables of linear congruential generators of different sizes and + * good lattice structure. Mathematics of Computation of the American + * Mathematical + * Society 68.225 (1999): 249-260. + */ + +static inline void lehmer64_seed(uint64_t seed) { + g_lehmer64_state.high = 0; + g_lehmer64_state.low = seed; +} + +static inline uint64_t lehmer64() { + fast_float::value128 v = fast_float::full_multiplication(g_lehmer64_state.low,UINT64_C(0xda942042e4dd58b5)); + v.high += g_lehmer64_state.high * UINT64_C(0xda942042e4dd58b5); + g_lehmer64_state = v; + return v.high; +} + +size_t errors; + +void random_values(size_t N) { + char buffer[128]; + lehmer64_seed(N); + for (size_t t = 0; t < N; t++) { + if ((t % 1048576) == 0) { + std::cout << "."; + std::cout.flush(); + } + uint64_t word = lehmer64(); + double v; + memcpy(&v, &word, sizeof(v)); + { + const char *string_end = to_string(v, buffer); + double result_value; + auto result = fast_float::from_chars(buffer, string_end, result_value); + if (result.ec != std::errc()) { + std::cerr << "parsing error ? " << buffer << std::endl; + errors++; + if (errors > 10) { + abort(); + } + } + if (std::isnan(v)) { + if (!std::isnan(result_value)) { + std::cerr << "not nan" << buffer << std::endl; + errors++; + if (errors > 10) { + abort(); + } + } + } else if(copysign(1,result_value) != copysign(1,v)) { + std::cerr << buffer << std::endl; + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v + << std::endl; + abort(); + } else if (result_value != v) { + std::cerr << "no match ? '" << buffer << "'" << std::endl; + std::cout << "started with " << std::hexfloat << v << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << std::dec; + errors++; + if (errors > 10) { + abort(); + } + } + } + } + std::cout << std::endl; +} + +int main() { + errors = 0; + size_t N = size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit + random_values(N); + if (errors == 0) { + std::cout << std::endl; + std::cout << "all ok" << std::endl; + return EXIT_SUCCESS; + } + std::cerr << std::endl; + std::cerr << "errors were encountered" << std::endl; + return EXIT_FAILURE; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_test.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_test.cpp new file mode 100644 index 000000000..36b92104e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_test.cpp @@ -0,0 +1,58 @@ +#include "fast_float/fast_float.h" + +#include <cctype> +#include <iostream> +#include <stdexcept> +#include <string> +#include <system_error> +#include <vector> + +inline void Assert(bool Assertion) { + if (!Assertion) { throw std::runtime_error("bug"); } +} + +template <typename T> +bool test() { + std::string input = "0.156250000000000000000000000000000000000000 3.14159265358979323846264338327950288419716939937510 2.71828182845904523536028747135266249775724709369995"; + std::vector<T> answers = {T(0.15625), T(3.141592653589793), T(2.718281828459045)}; + const char * begin = input.data(); + const char * end = input.data() + input.size(); + for(size_t i = 0; i < answers.size(); i++) { + T result_value; + while((begin < end) && (std::isspace(*begin))) { begin++; } + auto result = fast_float::from_chars(begin, end, + result_value); + if (result.ec != std::errc()) { + printf("parsing %.*s\n", int(end - begin), begin); + std::cerr << " I could not parse " << std::endl; + return false; + } + if(result_value != answers[i]) { + printf("parsing %.*s\n", int(end - begin), begin); + std::cerr << " Mismatch " << std::endl; + std::cerr << " Expected " << answers[i] << std::endl; + std::cerr << " Got " << result_value << std::endl; + + return false; + + } + begin = result.ptr; + } + if(begin != end) { + std::cerr << " bad ending " << std::endl; + return false; + } + return true; +} + +int main() { + + std::cout << "32 bits checks" << std::endl; + Assert(test<float>()); + + std::cout << "64 bits checks" << std::endl; + Assert(test<double>()); + + std::cout << "All ok" << std::endl; + return EXIT_SUCCESS; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/powersoffive_hardround.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/powersoffive_hardround.cpp new file mode 100644 index 000000000..09b95bd59 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/powersoffive_hardround.cpp @@ -0,0 +1,134 @@ +#include "fast_float/fast_float.h" + +#include <ios> +#include <iostream> +#include <random> +#include <sstream> +#include <string> +#include <system_error> +#include <utility> +#include <vector> + + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) +// Anything at all that is related to cygwin, msys and so forth will +// always use this fallback because we cannot rely on it behaving as normal +// gcc. +#include <locale> +// workaround for CYGWIN +double cygwin_strtod_l(const char* start, char** end) { + double d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +float cygwin_strtof_l(const char* start, char** end) { + float d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +#endif + + +std::pair<double, bool> strtod_from_string(const char *st) { + double d; + char *pr; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + d = cygwin_strtod_l(st, &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtod_l(st, &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtod_l(st, &pr, c_locale); +#endif + if (st == pr) { + std::cerr << "strtod_l could not parse '" << st << std::endl; + return std::make_pair(0, false); + } + return std::make_pair(d, true); +} + +std::pair<float, bool> strtof_from_string(char *st) { + float d; + char *pr; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + d = cygwin_strtof_l(st, &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtof_l(st, &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtof_l(st, &pr, c_locale); +#endif + if (st == pr) { + std::cerr << "strtof_l could not parse '" << st << std::endl; + return std::make_pair(0.0f, false); + } + return std::make_pair(d, true); +} + +bool tester() { + std::random_device rd; + std::mt19937 gen(rd()); + for (int q = 18; q <= 27; q++) { + std::cout << "q = " << -q << std::endl; + uint64_t power5 = 1; + for (int k = 0; k < q; k++) { + power5 *= 5; + } + uint64_t low_threshold = 0x20000000000000 / power5 + 1; + uint64_t threshold = 0xFFFFFFFFFFFFFFFF / power5; + std::uniform_int_distribution<uint64_t> dis(low_threshold, threshold); + for (size_t i = 0; i < 10000; i++) { + uint64_t mantissa = dis(gen) * power5; + std::stringstream ss; + ss << mantissa; + ss << "e"; + ss << -q; + std::string to_be_parsed = ss.str(); + std::pair<double, bool> expected_double = + strtod_from_string(to_be_parsed.c_str()); + double result_value; + auto result = + fast_float::from_chars(to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(), result_value); + if (result.ec != std::errc()) { + std::cout << to_be_parsed << std::endl; + std::cerr << " I could not parse " << std::endl; + return false; + } + if (result_value != expected_double.first) { + std::cout << to_be_parsed << std::endl; + std::cerr << std::hexfloat << result_value << std::endl; + std::cerr << std::hexfloat << expected_double.first << std::endl; + std::cerr << " Mismatch " << std::endl; + return false; + } + } + } + return true; +} + +int main() { + if (tester()) { + std::cout << std::endl; + std::cout << "all ok" << std::endl; + return EXIT_SUCCESS; + } + std::cerr << std::endl; + std::cerr << "errors were encountered" << std::endl; + return EXIT_FAILURE; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/random64.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/random64.cpp new file mode 100644 index 000000000..0775993a4 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/random64.cpp @@ -0,0 +1,109 @@ +#include "fast_float/fast_float.h" + +#include <cassert> +#include <cmath> +#include <cstdio> +#include <ios> +#include <iostream> +#include <limits> +#include <system_error> + +template <typename T> char *to_string(T d, char *buffer) { + auto written = std::snprintf(buffer, 64, "%.*e", + std::numeric_limits<T>::max_digits10 - 1, d); + return buffer + written; +} + + +static fast_float::value128 g_lehmer64_state; + +/** + * D. H. Lehmer, Mathematical methods in large-scale computing units. + * Proceedings of a Second Symposium on Large Scale Digital Calculating + * Machinery; + * Annals of the Computation Laboratory, Harvard Univ. 26 (1951), pp. 141-146. + * + * P L'Ecuyer, Tables of linear congruential generators of different sizes and + * good lattice structure. Mathematics of Computation of the American + * Mathematical + * Society 68.225 (1999): 249-260. + */ + +static inline void lehmer64_seed(uint64_t seed) { + g_lehmer64_state.high = 0; + g_lehmer64_state.low = seed; +} + +static inline uint64_t lehmer64() { + fast_float::value128 v = fast_float::full_multiplication(g_lehmer64_state.low,UINT64_C(0xda942042e4dd58b5)); + v.high += g_lehmer64_state.high * UINT64_C(0xda942042e4dd58b5); + g_lehmer64_state = v; + return v.high; +} + +size_t errors; + +void random_values(size_t N) { + char buffer[64]; + lehmer64_seed(N); + for (size_t t = 0; t < N; t++) { + if ((t % 1048576) == 0) { + std::cout << "."; + std::cout.flush(); + } + uint64_t word = lehmer64(); + double v; + memcpy(&v, &word, sizeof(v)); + // if (!std::isnormal(v)) + { + const char *string_end = to_string(v, buffer); + double result_value; + auto result = fast_float::from_chars(buffer, string_end, result_value); + if (result.ec != std::errc()) { + std::cerr << "parsing error ? " << buffer << std::endl; + errors++; + if (errors > 10) { + abort(); + } + } + if (std::isnan(v)) { + if (!std::isnan(result_value)) { + std::cerr << "not nan" << buffer << std::endl; + errors++; + if (errors > 10) { + abort(); + } + } + } else if(copysign(1,result_value) != copysign(1,v)) { + std::cerr << buffer << std::endl; + std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v + << std::endl; + abort(); + } else if (result_value != v) { + std::cerr << "no match ? " << buffer << std::endl; + std::cout << "started with " << std::hexfloat << v << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << std::dec; + errors++; + if (errors > 10) { + abort(); + } + } + } + } + std::cout << std::endl; +} + +int main() { + errors = 0; + size_t N = size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit + random_values(N); + if (errors == 0) { + std::cout << std::endl; + std::cout << "all ok" << std::endl; + return EXIT_SUCCESS; + } + std::cerr << std::endl; + std::cerr << "errors were encountered" << std::endl; + return EXIT_FAILURE; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/random_string.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/random_string.cpp new file mode 100644 index 000000000..8cabf5f4d --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/random_string.cpp @@ -0,0 +1,240 @@ +#include "fast_float/fast_float.h" + +#include <cstdint> +#include <ios> +#include <iostream> +#include <random> +#include <system_error> +#include <utility> + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) +// Anything at all that is related to cygwin, msys and so forth will +// always use this fallback because we cannot rely on it behaving as normal +// gcc. +#include <locale> +#include <sstream> +// workaround for CYGWIN +double cygwin_strtod_l(const char* start, char** end) { + double d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +float cygwin_strtof_l(const char* start, char** end) { + float d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +#endif + +class RandomEngine { +public: + RandomEngine() = delete; + RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) {}; + uint64_t next() { + // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h + // Inspired from + // https://github.com/lemire/testingRNG/blob/master/source/wyhash.h + wyhash64_x_ += UINT64_C(0x60bee2bee120fc15); + fast_float::value128 tmp = fast_float::full_multiplication(wyhash64_x_, UINT64_C(0xa3b195354a39b70d)); + uint64_t m1 = (tmp.high) ^ tmp.low; + tmp = fast_float::full_multiplication(m1, UINT64_C(0x1b03738712fad5c9)); + uint64_t m2 = (tmp.high) ^ tmp.low; + return m2; + } + bool next_bool() { return (next() & 1) == 1; } + int next_int() { return static_cast<int>(next()); } + char next_char() { return static_cast<char>(next()); } + double next_double() { return static_cast<double>(next()); } + + int next_ranged_int(int min, int max) { // min and max are included + // Adapted from + // https://lemire.me/blog/2019/06/06/nearly-divisionless-random-integer-generation-on-various-systems/ + /* if (min == max) { + return min; + }*/ + uint64_t s = uint64_t(max - min + 1); + uint64_t x = next(); + fast_float::value128 m = fast_float::full_multiplication(x, s); + uint64_t l = m.low; + if (l < s) { + uint64_t t = -s % s; + while (l < t) { + x = next(); + m = fast_float::full_multiplication(x, s); + l = m.low; + } + } + return int(m.high) + min; + } + int next_digit() { return next_ranged_int(0, 9); } + +private: + uint64_t wyhash64_x_; +}; + +size_t build_random_string(RandomEngine &rand, char *buffer) { + size_t pos{0}; + if (rand.next_bool()) { + buffer[pos++] = '-'; + } + int number_of_digits = rand.next_ranged_int(1, 100); + if(number_of_digits == 100) { + // With low probability, we want to allow very long strings just to stress the system. + number_of_digits = rand.next_ranged_int(1, 2000); + } + int location_of_decimal_separator = rand.next_ranged_int(1, number_of_digits); + for (size_t i = 0; i < size_t(number_of_digits); i++) { + if (i == size_t(location_of_decimal_separator)) { + buffer[pos++] = '.'; + } + buffer[pos++] = char(rand.next_digit() + '0'); + } + if (rand.next_bool()) { + if (rand.next_bool()) { + buffer[pos++] = 'e'; + } else { + buffer[pos++] = 'E'; + } + if (rand.next_bool()) { + buffer[pos++] = '-'; + } else { + if (rand.next_bool()) { + buffer[pos++] = '+'; + } + } + number_of_digits = rand.next_ranged_int(1, 3); + for (size_t i = 0; i < size_t(number_of_digits); i++) { + buffer[pos++] = char(rand.next_digit() + '0'); + } + } + buffer[pos] = '\0'; // null termination + return pos; +} + +std::pair<double, bool> strtod_from_string(char *st) { + double d; + char *pr; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + d = cygwin_strtod_l(st, &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtod_l(st, &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtod_l(st, &pr, c_locale); +#endif + if (st == pr) { + std::cerr << "strtod_l could not parse '" << st << std::endl; + return std::make_pair(0, false); + } + return std::make_pair(d, true); +} + +std::pair<float, bool> strtof_from_string(char *st) { + float d; + char *pr; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + d = cygwin_strtof_l(st, &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtof_l(st, &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtof_l(st, &pr, c_locale); +#endif + if (st == pr) { + std::cerr << "strtof_l could not parse '" << st << std::endl; + return std::make_pair(0.0f, false); + } + return std::make_pair(d, true); +} + +/** + * We generate random strings and we try to parse them with both strtod/strtof, + * and we verify that we get the same answer with with fast_float::from_chars. + */ +bool tester(uint64_t seed, size_t volume) { + char buffer[4096]; // large buffer (can't overflow) + RandomEngine rand(seed); + for (size_t i = 0; i < volume; i++) { + if((i%100000) == 0) { std::cout << "."; std::cout.flush(); } + size_t length = build_random_string(rand, buffer); + std::pair<double, bool> expected_double = strtod_from_string(buffer); + if (expected_double.second) { + double result_value; + auto result = + fast_float::from_chars(buffer, buffer + length, result_value); + if (result.ec != std::errc()) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << " I could not parse " << std::endl; + return false; + } + if (result.ptr != buffer + length) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << " Did not get to the end " << std::endl; + return false; + } + if (result_value != expected_double.first) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << std::hexfloat << result_value << std::endl; + std::cerr << std::hexfloat << expected_double.first << std::endl; + std::cerr << " Mismatch " << std::endl; + return false; + } + } + std::pair<float, bool> expected_float = strtof_from_string(buffer); + if (expected_float.second) { + float result_value; + auto result = + fast_float::from_chars(buffer, buffer + length, result_value); + if (result.ec != std::errc()) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << " I could not parse " << std::endl; + return false; + } + if (result.ptr != buffer + length) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << " Did not get to the end " << std::endl; + return false; + } + if (result_value != expected_float.first) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << std::hexfloat << result_value << std::endl; + std::cerr << std::hexfloat << expected_float.first << std::endl; + std::cerr << " Mismatch " << std::endl; + return false; + } + } + } + return true; +} + +int main() { + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + std::cout << "Warning: msys/cygwin or solaris detected." << std::endl; + return EXIT_SUCCESS; +#else + if (tester(1234344, 100000000)) { + std::cout << "All tests ok." << std::endl; + return EXIT_SUCCESS; + } + std::cout << "Failure." << std::endl; + return EXIT_FAILURE; + +#endif +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/short_random_string.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/short_random_string.cpp new file mode 100644 index 000000000..3051b749d --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/short_random_string.cpp @@ -0,0 +1,234 @@ +#include "fast_float/fast_float.h" + +#include <cstdint> +#include <ios> +#include <iostream> +#include <random> +#include <system_error> +#include <utility> + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) +// Anything at all that is related to cygwin, msys and so forth will +// always use this fallback because we cannot rely on it behaving as normal +// gcc. +#include <locale> +#include <sstream> +// workaround for CYGWIN +double cygwin_strtod_l(const char* start, char** end) { + double d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +float cygwin_strtof_l(const char* start, char** end) { + float d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +#endif + +class RandomEngine { +public: + RandomEngine() = delete; + RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) { }; + uint64_t next() { + // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h + // Inspired from + // https://github.com/lemire/testingRNG/blob/master/source/wyhash.h + wyhash64_x_ += UINT64_C(0x60bee2bee120fc15); + fast_float::value128 tmp = fast_float::full_multiplication(wyhash64_x_, UINT64_C(0xa3b195354a39b70d)); + uint64_t m1 = (tmp.high) ^ tmp.low; + tmp = fast_float::full_multiplication(m1, UINT64_C(0x1b03738712fad5c9)); + uint64_t m2 = (tmp.high) ^ tmp.low; + return m2; + } + bool next_bool() { return (next() & 1) == 1; } + int next_int() { return static_cast<int>(next()); } + char next_char() { return static_cast<char>(next()); } + double next_double() { return static_cast<double>(next()); } + + int next_ranged_int(int min, int max) { // min and max are included + // Adapted from + // https://lemire.me/blog/2019/06/06/nearly-divisionless-random-integer-generation-on-various-systems/ + /* if (min == max) { + return min; + }*/ + uint64_t s = uint64_t(max - min + 1); + uint64_t x = next(); + fast_float::value128 m = fast_float::full_multiplication(x, s); + uint64_t l = m.low; + if (l < s) { + uint64_t t = -s % s; + while (l < t) { + x = next(); + m = fast_float::full_multiplication(x, s); + l = m.low; + } + } + return int(m.high) + min; + } + int next_digit() { return next_ranged_int(0, 9); } + +private: + uint64_t wyhash64_x_; +}; + +size_t build_random_string(RandomEngine &rand, char *buffer) { + size_t pos{0}; + if (rand.next_bool()) { + buffer[pos++] = '-'; + } + int number_of_digits = rand.next_ranged_int(1, 19); + int location_of_decimal_separator = rand.next_ranged_int(1, number_of_digits); + for (size_t i = 0; i < size_t(number_of_digits); i++) { + if (i == size_t(location_of_decimal_separator)) { + buffer[pos++] = '.'; + } + buffer[pos++] = char(rand.next_digit() + '0'); + } + if (rand.next_bool()) { + if (rand.next_bool()) { + buffer[pos++] = 'e'; + } else { + buffer[pos++] = 'E'; + } + if (rand.next_bool()) { + buffer[pos++] = '-'; + } else { + if (rand.next_bool()) { + buffer[pos++] = '+'; + } + } + number_of_digits = rand.next_ranged_int(1, 3); + for (size_t i = 0; i < size_t(number_of_digits); i++) { + buffer[pos++] = char(rand.next_digit() + '0'); + } + } + buffer[pos] = '\0'; // null termination + return pos; +} + +std::pair<double, bool> strtod_from_string(char *st) { + double d; + char *pr; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + d = cygwin_strtod_l(st, &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtod_l(st, &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtod_l(st, &pr, c_locale); +#endif + if (st == pr) { + std::cerr << "strtod_l could not parse '" << st << std::endl; + return std::make_pair(0, false); + } + return std::make_pair(d, true); +} + +std::pair<float, bool> strtof_from_string(char *st) { + float d; + char *pr; +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + d = cygwin_strtof_l(st, &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtof_l(st, &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtof_l(st, &pr, c_locale); +#endif + if (st == pr) { + std::cerr << "strtof_l could not parse '" << st << std::endl; + return std::make_pair(0.0f, false); + } + return std::make_pair(d, true); +} + +/** + * We generate random strings and we try to parse them with both strtod/strtof, + * and we verify that we get the same answer with with fast_float::from_chars. + */ +bool tester(uint64_t seed, size_t volume) { + char buffer[4096]; // large buffer (can't overflow) + RandomEngine rand(seed); + for (size_t i = 0; i < volume; i++) { + if((i%1000000) == 0) { std::cout << "."; std::cout.flush(); } + size_t length = build_random_string(rand, buffer); + std::pair<double, bool> expected_double = strtod_from_string(buffer); + if (expected_double.second) { + double result_value; + auto result = + fast_float::from_chars(buffer, buffer + length, result_value); + if (result.ec != std::errc()) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << " I could not parse " << std::endl; + return false; + } + if (result.ptr != buffer + length) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << " Did not get to the end " << std::endl; + return false; + } + if (result_value != expected_double.first) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << std::hexfloat << result_value << std::endl; + std::cerr << std::hexfloat << expected_double.first << std::endl; + std::cerr << " Mismatch " << std::endl; + return false; + } + } + std::pair<float, bool> expected_float = strtof_from_string(buffer); + if (expected_float.second) { + float result_value; + auto result = + fast_float::from_chars(buffer, buffer + length, result_value); + if (result.ec != std::errc()) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << " I could not parse " << std::endl; + return false; + } + if (result.ptr != buffer + length) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << " Did not get to the end " << std::endl; + return false; + } + if (result_value != expected_float.first) { + printf("parsing %.*s\n", int(length), buffer); + std::cerr << std::hexfloat << result_value << std::endl; + std::cerr << std::hexfloat << expected_float.first << std::endl; + std::cerr << " Mismatch " << std::endl; + return false; + } + } + } + return true; +} + +int main() { +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + std::cout << "Warning: msys/cygwin detected. This particular test is likely to generate false failures due to our reliance on the underlying runtime library." << std::endl; + return EXIT_SUCCESS; +#else + if (tester(1234344, 100000000)) { + std::cout << "All tests ok." << std::endl; + return EXIT_SUCCESS; + } + return EXIT_FAILURE; + +#endif +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/string_test.cpp b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/string_test.cpp new file mode 100644 index 000000000..05871dbe3 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/string_test.cpp @@ -0,0 +1,279 @@ +#include "fast_float/fast_float.h" + +#include <cctype> +#include <cstdio> +#include <iostream> +#include <limits> +#include <stdexcept> +#include <string> +#include <system_error> +#include <vector> + +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) +// Anything at all that is related to cygwin, msys and so forth will +// always use this fallback because we cannot rely on it behaving as normal +// gcc. +#include <locale> +#include <sstream> +// workaround for CYGWIN +double cygwin_strtod_l(const char* start, char** end) { + double d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +float cygwin_strtof_l(const char* start, char** end) { + float d; + std::stringstream ss; + ss.imbue(std::locale::classic()); + ss << start; + ss >> d; + if(ss.fail()) { *end = nullptr; } + if(ss.eof()) { ss.clear(); } + auto nread = ss.tellg(); + *end = const_cast<char*>(start) + nread; + return d; +} +#endif + +inline void Assert(bool Assertion) { +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + if (!Assertion) { std::cerr << "Omitting hard falure on msys/cygwin/sun systems."; } +#else + if (!Assertion) { throw std::runtime_error("bug"); } +#endif +} + +template <typename T> std::string to_string(T d) { + std::string s(64, '\0'); + auto written = std::snprintf(&s[0], s.size(), "%.*e", + std::numeric_limits<T>::max_digits10 - 1, d); + s.resize(size_t(written)); + return s; +} + +template <typename T> +bool test() { + std::string input = "0.1 1e1000 100000 3.14159265359 -1e-500 001 1e01 1e0000001 -inf"; + std::vector<T> answers = {T(0.1), std::numeric_limits<T>::infinity(), 100000, T(3.14159265359), -0.0, 1, 10, 10, -std::numeric_limits<T>::infinity()}; + const char * begin = input.data(); + const char * end = input.data() + input.size(); + for(size_t i = 0; i < answers.size(); i++) { + T result_value; + while((begin < end) && (std::isspace(*begin))) { begin++; } + auto result = fast_float::from_chars(begin, end, + result_value); + if (result.ec != std::errc()) { + printf("parsing %.*s\n", int(end - begin), begin); + std::cerr << " I could not parse " << std::endl; + return false; + } + if(result_value != answers[i]) { + printf("parsing %.*s\n", int(end - begin), begin); + std::cerr << " Mismatch " << std::endl; + return false; + + } + begin = result.ptr; + } + if(begin != end) { + std::cerr << " bad ending " << std::endl; + return false; + } + return true; +} + +template <typename T> +void strtod_from_string(const std::string &st, T& d); + +template <> +void strtod_from_string(const std::string &st, double& d) { + char *pr = (char *)st.c_str(); +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + d = cygwin_strtod_l(pr, &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtod_l(st.c_str(), &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtod_l(st.c_str(), &pr, c_locale); +#endif + if (pr == st.c_str()) { + throw std::runtime_error("bug in strtod_from_string"); + } +} + +template <> +void strtod_from_string(const std::string &st, float& d) { + char *pr = (char *)st.c_str(); +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + d = cygwin_strtof_l(st.c_str(), &pr); +#elif defined(_WIN32) + static _locale_t c_locale = _create_locale(LC_ALL, "C"); + d = _strtof_l(st.c_str(), &pr, c_locale); +#else + static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); + d = strtof_l(st.c_str(), &pr, c_locale); +#endif + if (pr == st.c_str()) { + throw std::runtime_error("bug in strtod_from_string"); + } +} + +template <typename T> +bool partow_test() { + // credit: https://github.com/ArashPartow/strtk/blob/master/strtk_tokenizer_cmp.cpp#L568 + // MIT license + const std::string strint_list[] = { "9007199254740993", "9007199254740994", "9007199254740995" , + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "917049", "4931205", "6768064", "6884243", "5647132", "7371203", "-8629878", "4941840", "4543268", "1075600", + "290", "823", "111", "715", "-866", "367", "666", "-706", "850", "-161", + "9922547", "6960207", "1883152", "2300759", "-279294", "4187292", "3699841", "8386395", "-1441129", "-887892", + "-635422", "9742573", "2326186", "-5903851", "5648486", "3057647", "2980079", "2957468", "7929158", "1925615", + "879", "130", "292", "705", "817", "446", "576", "750", "523", "-527", + "4365041", "5624958", "8990205", "2652177", "3993588", "-298316", "2901599", "3887387", "-5202979", "1196268", + "5968501", "7619928", "3565643", "1885272", "-749485", "2961381", "2982579", "2387454", "4250081", "5958205", + "00000", "00001", "00002", "00003", "00004", "00005", "00006", "00007", "00008", "00009", + "4907034", "2592882", "3269234", "549815", "6256292", "9721039", "-595225", "5587491", "4596297", "-3885009", + "673", "-899", "174", "354", "870", "147", "898", "-510", "369", "859", + "6518423", "5149762", "8834164", "-8085586", "3233120", "8166948", "4172345", "6735549", "-934295", "9481935", + "-430406", "6932717", "4087292", "4047263", "3236400", "-3863050", "4312079", "6956261", "5689446", "3871332", + "535", "691", "326", "-409", "704", "-568", "301", "951", "121", "384", + "4969414", "9378599", "7971781", "5380630", "5001363", "1715827", "6044615", "9118925", "9956168", "-8865496", + "5962464", "7408980", "6646513", "-634564", "4188330", "9805948", "5625691", "7641113", "-4212929", "7802447", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "2174248", "7449361", "9896659", "-25961", "1706598", "2412368", "-4617035", "6314554", "2225957", "7521434", + "-9530566", "3914164", "2394759", "7157744", "9919392", "6406949", "-744004", "9899789", "8380325", "-1416284", + "3402833", "2150043", "5191009", "8979538", "9565778", "3750211", "7304823", "2829359", "6544236", "-615740", + "363", "-627", "129", "656", "135", "113", "381", "646", "198", "38", + "8060564", "-176752", "1184717", "-666343", "-1273292", "-485827", "6241066", "6579411", "8093119", "7481306", + "-4924485", "7467889", "9813178", "7927100", "3614859", "7293354", "9232973", "4323115", "1133911", "9511638", + "4443188", "2289448", "5639726", "9073898", "8540394", "5389992", "1397726", "-589230", "1017086", "1852330", + "-840", "267", "201", "533", "-675", "494", "315", "706", "-920", "784", + "9097353", "6002251", "-308780", "-3830169", "4340467", "2235284", "3314444", "1085967", "4152107", "5431117", + "-0000", "-0001", "-0002", "-0003", "-0004", "-0005", "-0006", "-0007", "-0008", "-0009", + "-444999", "2136400", "6925907", "6990614", "3588271", "8422028", "-4034772", "5804039", "-6740545", "9381873", + "-924923", "1652367", "2302616", "6776663", "2567821", "-248935", "2587688", "7076742", "-6461467", "1562896", + "-768116", "2338768", "9887307", "9992184", "2045182", "2797589", "9784597", "9696554", "5113329", "1067216", + "-76247763", "58169007", "29408062", "85342511", "42092201", "-95817703", "-1912517", "-26275135", "54656606", "-58188878", + "473", "74", "374", "-64", "266", "715", "937", "-249", "249", "780", + "3907360", "-23063423", "59062754", "83711047", "-95221044", "34894840", "-38562139", "-82018330", "14226223", "-10799717", + "8529722", "88961903", "25608618", "-39988247", "33228241", "38598533", "21161480", "-33723784", "8873948", "96505557", + "-47385048", "-79413272", "-85904404", "87791158", "49194195", "13051222", "57773302", "31904423", "3142966", "27846156", + "7420011", "-72376922", "-68873971", "23765361", "4040725", "-22359806", "85777219", "10099223", "-90364256", "-40158172", + "-7948696", "-64344821", "34404238", "84037448", "-85084788", "-42078409", "-56550310", "96898389", "-595829", "-73166703", + "-0", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9", + "2147483647", "31", "2147483610", "33", "2147483573", "37", "2147483536", + "-82838342", "64441808", "43641062", "-64419642", "-44421934", "75232413", "-75773725", "-89139509", "12812089", "-97633526", + "36090916", "-57706234", "17804655", "4189936", "-4100124", "38803710", "-39735126", "-62397437", "75801648", "51302332", + "73433906", "13015224", "-12624818", "91360377", "11576319", "-54467535", "8892431", "36319780", "38832042", "50172572", + "-317", "109", "-888", "302", "-463", "716", "916", "665", "826", "513", + "42423473", "41078812", "40445652", "-76722281", "95092224", "12075234", "-4045888", "-74396490", "-57304222", "-21726885", + "92038121", "-31899682", "21589254", "-30260046", "56000244", "69686659", "93327838", "96882881", "-91419389", "77529147", + "43288506", "1192435", "-74095920", "76756590", "-31184683", "-35716724", "9451980", "-63168350", "62864002", "26283194", + "37188395", "29151634", "99343471", "-69450330", "-55680090", "-64957599", "47577948", "47107924", "2490477", "48633003", + "-82740809", "-24122215", "67301713", "-63649610", "75499016", "82746620", "17052193", "4602244", "-32721165", "20837836", + "674", "467", "706", "889", "172", "282", "-795", "188", "87", "153", + "64501793", "53146328", "5152287", "-9674493", "68105580", "57245637", "39740229", "-74071854", "86777268", "86484437", + "-86962508", "12644427", "-62944073", "59539680", "43340539", "30661534", "20143968", "-68183731", "-48250926", "42669063", + "000", "001", "002", "003", "004", "005", "006", "007", "008", "009", + "2147483499", "71", "2147483462", "73", "2147483425", "77", "2147483388", + "87736852", "-4444906", "-48094147", "54774735", "54571890", "-22473078", "95053418", "393654", "-33229960", "32276798", + "-48361110", "44295939", "-79813406", "11630865", "38544571", "70972830", "-9821748", "-60965384", "-13096675", "-24569041", + "708", "-467", "-794", "610", "929", "766", "152", "482", "397", "-191", + "97233152", "51028396", "-13796948", "95437272", "71352512", "-83233730", "-68517318", "61832742", "-42667174", "-18002395", + "-92239407", "12701336", "-63830875", "41514172", "-5726049", "18668677", "69555144", "-13737009", "-22626233", "-55078143", + "00", "11", "22", "33", "44", "-00", "-11", "-22", "-33", "-44", + "000", "111", "222", "333", "444", "-000", "-111", "-222", "-333", "-444", + "0000", "1111", "2222", "3333", "4444", "-0000", "-1111", "-2222", "-3333", "-4444", + "00000", "11111", "22222", "33333", "44444", "-00000", "-11111", "-22222", "-33333", "-44444", + "000000", "111111", "222222", "333333", "444444", "-000000", "-111111", "-222222", "-333333", "-444444", + "0000000", "1111111", "2222222", "3333333", "4444444", "-0000000", "-1111111", "-2222222", "-3333333", "-4444444", + "00000000", "11111111", "22222222", "33333333", "44444444", "-00000000", "-11111111", "-22222222", "-33333333", "-44444444", + "000000000", "111111111", "222222222", "333333333", "444444444","-000000000","-111111111","-222222222","-333333333","-444444444", + "2147483351", "51", "2147483314", "53", "-2147483648", "57", "-2147483611", + "55", "66", "77", "88", "99", "-55", "-66", "-77", "-88", "-99", + "555", "666", "777", "888", "999", "-555", "-666", "-777", "-888", "-999", + "5555", "6666", "7777", "8888", "9999", "-5555", "-6666", "-7777", "-8888", "-9999", + "55555", "66666", "77777", "88888", "99999", "-55555", "-66666", "-77777", "-88888", "-99999", + "555555", "666666", "777777", "888888", "999999", "-555555", "-666666", "-777777", "-888888", "-999999", + "5555555", "6666666", "7777777", "8888888", "9999999", "-5555555", "-6666666", "-7777777", "-8888888", "-9999999", + "55555555", "66666666", "77777777", "88888888", "99999999", "-55555555", "-66666666", "-77777777", "-88888888", "-99999999", + "555555555", "666666666", "777777777", "888888888", "999999999","-555555555","-666666666","-777777777","-888888888","-999999999", + "-2147483574", "91", "-2147483537", "93", "-2147483500", "97", "-2147483463", + "0000000011", "0000000022", "0000000033", "0000000044", "-000000011", "-000000022", "-000000033", "-000000044", "-000000088", + "0000000111", "0000000222", "0000000333", "0000000444", "-000000111", "-000000222", "-000000333", "-000000444", "-000000888", + "0000001111", "0000002222", "0000003333", "0000004444", "-000001111", "-000002222", "-000003333", "-000004444", "-000008888", + "0000011111", "0000022222", "0000033333", "0000044444", "-000011111", "-000022222", "-000033333", "-000044444", "-000088888", + "0000111111", "0000222222", "0000333333", "0000444444", "-000111111", "-000222222", "-000333333", "-000444444", "-000888888", + "0001111111", "0002222222", "0003333333", "0004444444", "-001111111", "-002222222", "-003333333", "-004444444", "-008888888", + "0011111111", "0022222222", "0033333333", "0044444444", "-011111111", "-022222222", "-033333333", "-044444444", "-088888888", + "0111111111", "0222222222", "0333333333", "0444444444", "-111111111", "-222222222", "-333333333", "-444444444", "-888888888", + "0000000055", "0000000066", "0000000077", "0000000088", "0000000099", "-000000055", "-000000066", "-000000077", "-000000099", + "0000000555", "0000000666", "0000000777", "0000000888", "0000000999", "-000000555", "-000000666", "-000000777", "-000000999", + "0000005555", "0000006666", "0000007777", "0000008888", "0000009999", "-000005555", "-000006666", "-000007777", "-000009999", + "0000055555", "0000066666", "0000077777", "0000088888", "0000099999", "-000055555", "-000066666", "-000077777", "-000099999", + "0000555555", "0000666666", "0000777777", "0000888888", "0000999999", "-000555555", "-000666666", "-000777777", "-000999999", + "0005555555", "0006666666", "0007777777", "0008888888", "0009999999", "-005555555", "-006666666", "-007777777", "-009999999", + "0055555555", "0066666666", "0077777777", "0088888888", "0099999999", "-055555555", "-066666666", "-077777777", "-099999999", + "0555555555", "0666666666", "0777777777", "0888888888", "0999999999", "-555555555", "-666666666", "-777777777", "-999999999", + "-2147483426", "101", "-2147483389", "103", "-2147483352", "105", "-2147483315", + "0000001234567890", "0000001234567890", "-0000001234567890", + "000001234567890", "000001234567890", "-000001234567890", + "00001234567890", "00001234567890", "-00001234567890", + "0001234567890", "0001234567890", "-0001234567890", + "001234567890", "001234567890", "-001234567890", + "01234567890", "01234567890", "-01234567890", + "1234567890", "1234567890", "-1234567890", + }; + for(const std::string& st : strint_list) { + T expected_value; + strtod_from_string(st, expected_value); + T result_value; + auto result = fast_float::from_chars(st.data(), st.data() + st.size(), + result_value); + if (result.ec != std::errc()) { + printf("parsing %.*s\n", int(st.size()), st.data()); + std::cerr << " I could not parse " << std::endl; + return false; + } + if(result.ptr != st.data() + st.size()) { + printf("parsing %.*s\n", int(st.size()), st.data()); + std::cerr << " Did not get to the end " << std::endl; + return false; + } + if(result_value != expected_value) { + printf("parsing %.*s\n", int(st.size()), st.data()); + std::cerr << "expected value : " << to_string(expected_value) << std::endl; + std::cerr << "result value : " << to_string(result_value) << std::endl; + std::cerr << " Mismatch " << std::endl; + return false; + } + + } + return true; + +} + + +int main() { +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) + std::cout << "Warning: msys/cygwin or solaris detected." << std::endl; +#endif + std::cout << "32 bits checks" << std::endl; + Assert(partow_test<float>()); + Assert(test<float>()); + + std::cout << "64 bits checks" << std::endl; + Assert(partow_test<double>()); + Assert(test<double>()); + + std::cout << "All ok" << std::endl; + return EXIT_SUCCESS; +} diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float_all.h b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float_all.h new file mode 100644 index 000000000..098aa17ab --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/fast_float_all.h @@ -0,0 +1,2947 @@ +// fast_float by Daniel Lemire +// fast_float by João Paulo Magalhaes + + +// with contributions from Eugene Golushkov +// with contributions from Maksim Kita +// with contributions from Marcin Wojdyr +// with contributions from Neal Richardson +// with contributions from Tim Paine +// with contributions from Fabio Pellacini + + +// 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 FASTFLOAT_FAST_FLOAT_H +#define FASTFLOAT_FAST_FLOAT_H + +#include <system_error> + +namespace fast_float { +enum chars_format { + scientific = 1<<0, + fixed = 1<<2, + hex = 1<<3, + general = fixed | scientific +}; + + +struct from_chars_result { + const char *ptr; + std::errc ec; +}; + +struct parse_options { + constexpr explicit parse_options(chars_format fmt = chars_format::general, + char dot = '.') + : format(fmt), decimal_point(dot) {} + + /** Which number formats are accepted */ + chars_format format; + /** The character used as decimal point */ + char decimal_point; +}; + +/** + * This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting + * a locale-indepent format equivalent to what is used by std::strtod in the default ("C") locale. + * The resulting floating-point value is the closest floating-point values (using either float or double), + * using the "round to even" convention for values that would otherwise fall right in-between two values. + * That is, we provide exact parsing according to the IEEE standard. + * + * Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the + * parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned + * `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored. + * + * The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`). + * + * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of + * the type `fast_float::chars_format`. It is a bitset value: we check whether + * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set + * to determine whether we allowe the fixed point and scientific notation respectively. + * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. + */ +template<typename T> +from_chars_result from_chars(const char *first, const char *last, + T &value, chars_format fmt = chars_format::general) noexcept; + +/** + * Like from_chars, but accepts an `options` argument to govern number parsing. + */ +template<typename T> +from_chars_result from_chars_advanced(const char *first, const char *last, + T &value, parse_options options) noexcept; + +} +#endif // FASTFLOAT_FAST_FLOAT_H + + +#ifndef FASTFLOAT_FLOAT_COMMON_H +#define FASTFLOAT_FLOAT_COMMON_H + +#include <cfloat> +#include <cstdint> +#include <cassert> +#include <cstring> + +#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ + || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ + || defined(__MINGW64__) \ + || defined(__s390x__) \ + || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \ + || defined(__EMSCRIPTEN__)) +#define FASTFLOAT_64BIT +#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ + || defined(__arm__) || defined(_M_ARM) \ + || defined(__MINGW32__)) +#define FASTFLOAT_32BIT +#else + // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. + // We can never tell the register width, but the SIZE_MAX is a good approximation. + // UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max portability. + #if SIZE_MAX == 0xffff + #error Unknown platform (16-bit, unsupported) + #elif SIZE_MAX == 0xffffffff + #define FASTFLOAT_32BIT + #elif SIZE_MAX == 0xffffffffffffffff + #define FASTFLOAT_64BIT + #else + #error Unknown platform (not 32-bit, not 64-bit?) + #endif +#endif + +#if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) +#include <intrin.h> +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#define FASTFLOAT_VISUAL_STUDIO 1 +#endif + +#ifdef _WIN32 +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#if defined(__APPLE__) || defined(__FreeBSD__) +#include <machine/endian.h> +#elif defined(sun) || defined(__sun) +#include <sys/byteorder.h> +#else +#include <endian.h> +#endif +# +#ifndef __BYTE_ORDER__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#ifndef __ORDER_LITTLE_ENDIAN__ +// safe choice +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#endif +# +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define FASTFLOAT_IS_BIG_ENDIAN 0 +#else +#define FASTFLOAT_IS_BIG_ENDIAN 1 +#endif +#endif + +#ifdef FASTFLOAT_VISUAL_STUDIO +#define fastfloat_really_inline __forceinline +#else +#define fastfloat_really_inline inline __attribute__((always_inline)) +#endif + +#ifndef FASTFLOAT_ASSERT +#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); } +#endif + +#ifndef FASTFLOAT_DEBUG_ASSERT +#include <cassert> +#define FASTFLOAT_DEBUG_ASSERT(x) assert(x) +#endif + +// rust style `try!()` macro, or `?` operator +#define FASTFLOAT_TRY(x) { if (!(x)) return false; } + +namespace fast_float { + +// Compares two ASCII strings in a case insensitive manner. +inline bool fastfloat_strncasecmp(const char *input1, const char *input2, + size_t length) { + char running_diff{0}; + for (size_t i = 0; i < length; i++) { + running_diff |= (input1[i] ^ input2[i]); + } + return (running_diff == 0) || (running_diff == 32); +} + +#ifndef FLT_EVAL_METHOD +#error "FLT_EVAL_METHOD should be defined, please include cfloat." +#endif + +// a pointer and a length to a contiguous block of memory +template <typename T> +struct span { + const T* ptr; + size_t length; + span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {} + span() : ptr(nullptr), length(0) {} + + constexpr size_t len() const noexcept { + return length; + } + + const T& operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return ptr[index]; + } +}; + +struct value128 { + uint64_t low; + uint64_t high; + value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} + value128() : low(0), high(0) {} +}; + +/* result might be undefined when input_num is zero */ +fastfloat_really_inline int leading_zeroes(uint64_t input_num) { + assert(input_num > 0); +#ifdef FASTFLOAT_VISUAL_STUDIO + #if defined(_M_X64) || defined(_M_ARM64) + unsigned long leading_zero = 0; + // Search the mask data from most significant bit (MSB) + // to least significant bit (LSB) for a set bit (1). + _BitScanReverse64(&leading_zero, input_num); + return (int)(63 - leading_zero); + #else + int last_bit = 0; + if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32; + if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16; + if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8; + if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4; + if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2; + if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1; + return 63 - last_bit; + #endif +#else + return __builtin_clzll(input_num); +#endif +} + +#ifdef FASTFLOAT_32BIT + +// slow emulation routine for 32-bit +fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) { + return x * (uint64_t)y; +} + +// slow emulation routine for 32-bit +#if !defined(__MINGW64__) +fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, + uint64_t *hi) { + uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); + uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); + uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); + uint64_t adbc_carry = !!(adbc < ad); + uint64_t lo = bd + (adbc << 32); + *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + + (adbc_carry << 32) + !!(lo < bd); + return lo; +} +#endif // !__MINGW64__ + +#endif // FASTFLOAT_32BIT + + +// compute 64-bit a*b +fastfloat_really_inline value128 full_multiplication(uint64_t a, + uint64_t b) { + value128 answer; +#ifdef _M_ARM64 + // ARM64 has native support for 64-bit multiplications, no need to emulate + answer.high = __umulh(a, b); + answer.low = a * b; +#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__)) + answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 +#elif defined(FASTFLOAT_64BIT) + __uint128_t r = ((__uint128_t)a) * b; + answer.low = uint64_t(r); + answer.high = uint64_t(r >> 64); +#else + #error Not implemented +#endif + return answer; +} + +struct adjusted_mantissa { + uint64_t mantissa{0}; + int32_t power2{0}; // a negative value indicates an invalid result + adjusted_mantissa() = default; + bool operator==(const adjusted_mantissa &o) const { + return mantissa == o.mantissa && power2 == o.power2; + } + bool operator!=(const adjusted_mantissa &o) const { + return mantissa != o.mantissa || power2 != o.power2; + } +}; + +// Bias so we can get the real exponent with an invalid adjusted_mantissa. +constexpr static int32_t invalid_am_bias = -0x8000; + +constexpr static double powers_of_ten_double[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, + 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; +constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, + 1e6, 1e7, 1e8, 1e9, 1e10}; + +template <typename T> struct binary_format { + static inline constexpr int mantissa_explicit_bits(); + static inline constexpr int minimum_exponent(); + static inline constexpr int infinite_power(); + static inline constexpr int sign_index(); + static inline constexpr int min_exponent_fast_path(); + static inline constexpr int max_exponent_fast_path(); + static inline constexpr int max_exponent_round_to_even(); + static inline constexpr int min_exponent_round_to_even(); + static inline constexpr uint64_t max_mantissa_fast_path(); + static inline constexpr int largest_power_of_ten(); + static inline constexpr int smallest_power_of_ten(); + static inline constexpr T exact_power_of_ten(int64_t power); + static inline constexpr size_t max_digits(); +}; + +template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() { + return 52; +} +template <> inline constexpr int binary_format<float>::mantissa_explicit_bits() { + return 23; +} + +template <> inline constexpr int binary_format<double>::max_exponent_round_to_even() { + return 23; +} + +template <> inline constexpr int binary_format<float>::max_exponent_round_to_even() { + return 10; +} + +template <> inline constexpr int binary_format<double>::min_exponent_round_to_even() { + return -4; +} + +template <> inline constexpr int binary_format<float>::min_exponent_round_to_even() { + return -17; +} + +template <> inline constexpr int binary_format<double>::minimum_exponent() { + return -1023; +} +template <> inline constexpr int binary_format<float>::minimum_exponent() { + return -127; +} + +template <> inline constexpr int binary_format<double>::infinite_power() { + return 0x7FF; +} +template <> inline constexpr int binary_format<float>::infinite_power() { + return 0xFF; +} + +template <> inline constexpr int binary_format<double>::sign_index() { return 63; } +template <> inline constexpr int binary_format<float>::sign_index() { return 31; } + +template <> inline constexpr int binary_format<double>::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -22; +#endif +} +template <> inline constexpr int binary_format<float>::min_exponent_fast_path() { +#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) + return 0; +#else + return -10; +#endif +} + +template <> inline constexpr int binary_format<double>::max_exponent_fast_path() { + return 22; +} +template <> inline constexpr int binary_format<float>::max_exponent_fast_path() { + return 10; +} + +template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} +template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() { + return uint64_t(2) << mantissa_explicit_bits(); +} + +template <> +inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) { + return powers_of_ten_double[power]; +} +template <> +inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) { + + return powers_of_ten_float[power]; +} + + +template <> +inline constexpr int binary_format<double>::largest_power_of_ten() { + return 308; +} +template <> +inline constexpr int binary_format<float>::largest_power_of_ten() { + return 38; +} + +template <> +inline constexpr int binary_format<double>::smallest_power_of_ten() { + return -342; +} +template <> +inline constexpr int binary_format<float>::smallest_power_of_ten() { + return -65; +} + +template <> inline constexpr size_t binary_format<double>::max_digits() { + return 769; +} +template <> inline constexpr size_t binary_format<float>::max_digits() { + return 114; +} + +template<typename T> +fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { + uint64_t word = am.mantissa; + word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits(); + word = negative + ? word | (uint64_t(1) << binary_format<T>::sign_index()) : word; +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + if (std::is_same<T, float>::value) { + ::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian + } else { + ::memcpy(&value, &word, sizeof(T)); + } +#else + // For little-endian systems: + ::memcpy(&value, &word, sizeof(T)); +#endif +} + +} // namespace fast_float + +#endif + + +#ifndef FASTFLOAT_ASCII_NUMBER_H +#define FASTFLOAT_ASCII_NUMBER_H + +#include <cctype> +#include <cstdint> +#include <cstring> +#include <iterator> + + +namespace fast_float { + +// Next function can be micro-optimized, but compilers are entirely +// able to optimize it well. +fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } + +fastfloat_really_inline uint64_t byteswap(uint64_t val) { + return (val & 0xFF00000000000000) >> 56 + | (val & 0x00FF000000000000) >> 40 + | (val & 0x0000FF0000000000) >> 24 + | (val & 0x000000FF00000000) >> 8 + | (val & 0x00000000FF000000) << 8 + | (val & 0x0000000000FF0000) << 24 + | (val & 0x000000000000FF00) << 40 + | (val & 0x00000000000000FF) << 56; +} + +fastfloat_really_inline uint64_t read_u64(const char *chars) { + uint64_t val; + ::memcpy(&val, chars, sizeof(uint64_t)); +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + return val; +} + +fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + ::memcpy(chars, &val, sizeof(uint64_t)); +} + +// credit @aqrit +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { + const uint64_t mask = 0x000000FF000000FF; + const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return uint32_t(val); +} + +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { + return parse_eight_digits_unrolled(read_u64(chars)); +} + +// credit @aqrit +fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { + return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & + 0x8080808080808080)); +} + +fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { + return is_made_of_eight_digits_fast(read_u64(chars)); +} + +typedef span<const char> byte_span; + +struct parsed_number_string { + int64_t exponent{0}; + uint64_t mantissa{0}; + const char *lastmatch{nullptr}; + bool negative{false}; + bool valid{false}; + bool too_many_digits{false}; + // contains the range of the significant digits + byte_span integer{}; // non-nullable + byte_span fraction{}; // nullable +}; + +// Assuming that you use no more than 19 digits, this will +// parse an ASCII string. +fastfloat_really_inline +parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { + const chars_format fmt = options.format; + const char decimal_point = options.decimal_point; + + parsed_number_string answer; + answer.valid = false; + answer.too_many_digits = false; + answer.negative = (*p == '-'); + if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + ++p; + if (p == pend) { + return answer; + } + if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot + return answer; + } + } + const char *const start_digits = p; + + uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) + + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + + uint64_t(*p - '0'); // might overflow, we will handle the overflow later + ++p; + } + const char *const end_of_integer_part = p; + int64_t digit_count = int64_t(end_of_integer_part - start_digits); + answer.integer = byte_span(start_digits, size_t(digit_count)); + int64_t exponent = 0; + if ((p != pend) && (*p == decimal_point)) { + ++p; + const char* before = p; + // can occur at most twice without overflowing, but let it occur more, since + // for integers with many digits, digit parsing is the primary bottleneck. + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + ++p; + i = i * 10 + digit; // in rare cases, this will overflow, but that's ok + } + exponent = before - p; + answer.fraction = byte_span(before, size_t(p - before)); + digit_count -= exponent; + } + // we must have encountered at least one integer! + if (digit_count == 0) { + return answer; + } + int64_t exp_number = 0; // explicit exponential part + if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { + const char * location_of_e = p; + ++p; + bool neg_exp = false; + if ((p != pend) && ('-' == *p)) { + neg_exp = true; + ++p; + } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) + ++p; + } + if ((p == pend) || !is_integer(*p)) { + if(!(fmt & chars_format::fixed)) { + // We are in error. + return answer; + } + // Otherwise, we will be ignoring the 'e'. + p = location_of_e; + } else { + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + if (exp_number < 0x10000000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + if(neg_exp) { exp_number = - exp_number; } + exponent += exp_number; + } + } else { + // If it scientific and not fixed, we have to bail out. + if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; } + } + answer.lastmatch = p; + answer.valid = true; + + // If we frequently had to deal with long strings of digits, + // we could extend our code by using a 128-bit integer instead + // of a 64-bit integer. However, this is uncommon. + // + // We can deal with up to 19 digits. + if (digit_count > 19) { // this is uncommon + // It is possible that the integer had an overflow. + // We have to handle the case where we have 0.0000somenumber. + // We need to be mindful of the case where we only have zeroes... + // E.g., 0.000000000...000. + const char *start = start_digits; + while ((start != pend) && (*start == '0' || *start == decimal_point)) { + if(*start == '0') { digit_count --; } + start++; + } + if (digit_count > 19) { + answer.too_many_digits = true; + // Let us start again, this time, avoiding overflows. + // We don't need to check if is_integer, since we use the + // pre-tokenized spans from above. + i = 0; + p = answer.integer.ptr; + const char* int_end = p + answer.integer.len(); + const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; + while((i < minimal_nineteen_digit_integer) && (p != int_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + if (i >= minimal_nineteen_digit_integer) { // We have a big integers + exponent = end_of_integer_part - p + exp_number; + } else { // We have a value with a fractional component. + p = answer.fraction.ptr; + const char* frac_end = p + answer.fraction.len(); + while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + exponent = answer.fraction.ptr - p + exp_number; + } + // We have now corrected both exponent and i, to a truncated value + } + } + answer.exponent = exponent; + answer.mantissa = i; + return answer; +} + +} // namespace fast_float + +#endif + + +#ifndef FASTFLOAT_FAST_TABLE_H +#define FASTFLOAT_FAST_TABLE_H + +#include <cstdint> + +namespace fast_float { + +/** + * When mapping numbers from decimal to binary, + * we go from w * 10^q to m * 2^p but we have + * 10^q = 5^q * 2^q, so effectively + * we are trying to match + * w * 2^q * 5^q to m * 2^p. Thus the powers of two + * are not a concern since they can be represented + * exactly using the binary notation, only the powers of five + * affect the binary significand. + */ + +/** + * The smallest non-zero float (binary64) is 2^−1074. + * We take as input numbers of the form w x 10^q where w < 2^64. + * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. + * However, we have that + * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^−1074. + * Thus it is possible for a number of the form w * 10^-342 where + * w is a 64-bit value to be a non-zero floating-point number. + ********* + * Any number of form w * 10^309 where w>= 1 is going to be + * infinite in binary64 so we never need to worry about powers + * of 5 greater than 308. + */ +template <class unused = void> +struct powers_template { + +constexpr static int smallest_power_of_five = binary_format<double>::smallest_power_of_ten(); +constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten(); +constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1); +// Powers of five from 5^-342 all the way to 5^308 rounded toward one. +static const uint64_t power_of_five_128[number_of_entries]; +}; + +template <class unused> +const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = { + 0xeef453d6923bd65a,0x113faa2906a13b3f, + 0x9558b4661b6565f8,0x4ac7ca59a424c507, + 0xbaaee17fa23ebf76,0x5d79bcf00d2df649, + 0xe95a99df8ace6f53,0xf4d82c2c107973dc, + 0x91d8a02bb6c10594,0x79071b9b8a4be869, + 0xb64ec836a47146f9,0x9748e2826cdee284, + 0xe3e27a444d8d98b7,0xfd1b1b2308169b25, + 0x8e6d8c6ab0787f72,0xfe30f0f5e50e20f7, + 0xb208ef855c969f4f,0xbdbd2d335e51a935, + 0xde8b2b66b3bc4723,0xad2c788035e61382, + 0x8b16fb203055ac76,0x4c3bcb5021afcc31, + 0xaddcb9e83c6b1793,0xdf4abe242a1bbf3d, + 0xd953e8624b85dd78,0xd71d6dad34a2af0d, + 0x87d4713d6f33aa6b,0x8672648c40e5ad68, + 0xa9c98d8ccb009506,0x680efdaf511f18c2, + 0xd43bf0effdc0ba48,0x212bd1b2566def2, + 0x84a57695fe98746d,0x14bb630f7604b57, + 0xa5ced43b7e3e9188,0x419ea3bd35385e2d, + 0xcf42894a5dce35ea,0x52064cac828675b9, + 0x818995ce7aa0e1b2,0x7343efebd1940993, + 0xa1ebfb4219491a1f,0x1014ebe6c5f90bf8, + 0xca66fa129f9b60a6,0xd41a26e077774ef6, + 0xfd00b897478238d0,0x8920b098955522b4, + 0x9e20735e8cb16382,0x55b46e5f5d5535b0, + 0xc5a890362fddbc62,0xeb2189f734aa831d, + 0xf712b443bbd52b7b,0xa5e9ec7501d523e4, + 0x9a6bb0aa55653b2d,0x47b233c92125366e, + 0xc1069cd4eabe89f8,0x999ec0bb696e840a, + 0xf148440a256e2c76,0xc00670ea43ca250d, + 0x96cd2a865764dbca,0x380406926a5e5728, + 0xbc807527ed3e12bc,0xc605083704f5ecf2, + 0xeba09271e88d976b,0xf7864a44c633682e, + 0x93445b8731587ea3,0x7ab3ee6afbe0211d, + 0xb8157268fdae9e4c,0x5960ea05bad82964, + 0xe61acf033d1a45df,0x6fb92487298e33bd, + 0x8fd0c16206306bab,0xa5d3b6d479f8e056, + 0xb3c4f1ba87bc8696,0x8f48a4899877186c, + 0xe0b62e2929aba83c,0x331acdabfe94de87, + 0x8c71dcd9ba0b4925,0x9ff0c08b7f1d0b14, + 0xaf8e5410288e1b6f,0x7ecf0ae5ee44dd9, + 0xdb71e91432b1a24a,0xc9e82cd9f69d6150, + 0x892731ac9faf056e,0xbe311c083a225cd2, + 0xab70fe17c79ac6ca,0x6dbd630a48aaf406, + 0xd64d3d9db981787d,0x92cbbccdad5b108, + 0x85f0468293f0eb4e,0x25bbf56008c58ea5, + 0xa76c582338ed2621,0xaf2af2b80af6f24e, + 0xd1476e2c07286faa,0x1af5af660db4aee1, + 0x82cca4db847945ca,0x50d98d9fc890ed4d, + 0xa37fce126597973c,0xe50ff107bab528a0, + 0xcc5fc196fefd7d0c,0x1e53ed49a96272c8, + 0xff77b1fcbebcdc4f,0x25e8e89c13bb0f7a, + 0x9faacf3df73609b1,0x77b191618c54e9ac, + 0xc795830d75038c1d,0xd59df5b9ef6a2417, + 0xf97ae3d0d2446f25,0x4b0573286b44ad1d, + 0x9becce62836ac577,0x4ee367f9430aec32, + 0xc2e801fb244576d5,0x229c41f793cda73f, + 0xf3a20279ed56d48a,0x6b43527578c1110f, + 0x9845418c345644d6,0x830a13896b78aaa9, + 0xbe5691ef416bd60c,0x23cc986bc656d553, + 0xedec366b11c6cb8f,0x2cbfbe86b7ec8aa8, + 0x94b3a202eb1c3f39,0x7bf7d71432f3d6a9, + 0xb9e08a83a5e34f07,0xdaf5ccd93fb0cc53, + 0xe858ad248f5c22c9,0xd1b3400f8f9cff68, + 0x91376c36d99995be,0x23100809b9c21fa1, + 0xb58547448ffffb2d,0xabd40a0c2832a78a, + 0xe2e69915b3fff9f9,0x16c90c8f323f516c, + 0x8dd01fad907ffc3b,0xae3da7d97f6792e3, + 0xb1442798f49ffb4a,0x99cd11cfdf41779c, + 0xdd95317f31c7fa1d,0x40405643d711d583, + 0x8a7d3eef7f1cfc52,0x482835ea666b2572, + 0xad1c8eab5ee43b66,0xda3243650005eecf, + 0xd863b256369d4a40,0x90bed43e40076a82, + 0x873e4f75e2224e68,0x5a7744a6e804a291, + 0xa90de3535aaae202,0x711515d0a205cb36, + 0xd3515c2831559a83,0xd5a5b44ca873e03, + 0x8412d9991ed58091,0xe858790afe9486c2, + 0xa5178fff668ae0b6,0x626e974dbe39a872, + 0xce5d73ff402d98e3,0xfb0a3d212dc8128f, + 0x80fa687f881c7f8e,0x7ce66634bc9d0b99, + 0xa139029f6a239f72,0x1c1fffc1ebc44e80, + 0xc987434744ac874e,0xa327ffb266b56220, + 0xfbe9141915d7a922,0x4bf1ff9f0062baa8, + 0x9d71ac8fada6c9b5,0x6f773fc3603db4a9, + 0xc4ce17b399107c22,0xcb550fb4384d21d3, + 0xf6019da07f549b2b,0x7e2a53a146606a48, + 0x99c102844f94e0fb,0x2eda7444cbfc426d, + 0xc0314325637a1939,0xfa911155fefb5308, + 0xf03d93eebc589f88,0x793555ab7eba27ca, + 0x96267c7535b763b5,0x4bc1558b2f3458de, + 0xbbb01b9283253ca2,0x9eb1aaedfb016f16, + 0xea9c227723ee8bcb,0x465e15a979c1cadc, + 0x92a1958a7675175f,0xbfacd89ec191ec9, + 0xb749faed14125d36,0xcef980ec671f667b, + 0xe51c79a85916f484,0x82b7e12780e7401a, + 0x8f31cc0937ae58d2,0xd1b2ecb8b0908810, + 0xb2fe3f0b8599ef07,0x861fa7e6dcb4aa15, + 0xdfbdcece67006ac9,0x67a791e093e1d49a, + 0x8bd6a141006042bd,0xe0c8bb2c5c6d24e0, + 0xaecc49914078536d,0x58fae9f773886e18, + 0xda7f5bf590966848,0xaf39a475506a899e, + 0x888f99797a5e012d,0x6d8406c952429603, + 0xaab37fd7d8f58178,0xc8e5087ba6d33b83, + 0xd5605fcdcf32e1d6,0xfb1e4a9a90880a64, + 0x855c3be0a17fcd26,0x5cf2eea09a55067f, + 0xa6b34ad8c9dfc06f,0xf42faa48c0ea481e, + 0xd0601d8efc57b08b,0xf13b94daf124da26, + 0x823c12795db6ce57,0x76c53d08d6b70858, + 0xa2cb1717b52481ed,0x54768c4b0c64ca6e, + 0xcb7ddcdda26da268,0xa9942f5dcf7dfd09, + 0xfe5d54150b090b02,0xd3f93b35435d7c4c, + 0x9efa548d26e5a6e1,0xc47bc5014a1a6daf, + 0xc6b8e9b0709f109a,0x359ab6419ca1091b, + 0xf867241c8cc6d4c0,0xc30163d203c94b62, + 0x9b407691d7fc44f8,0x79e0de63425dcf1d, + 0xc21094364dfb5636,0x985915fc12f542e4, + 0xf294b943e17a2bc4,0x3e6f5b7b17b2939d, + 0x979cf3ca6cec5b5a,0xa705992ceecf9c42, + 0xbd8430bd08277231,0x50c6ff782a838353, + 0xece53cec4a314ebd,0xa4f8bf5635246428, + 0x940f4613ae5ed136,0x871b7795e136be99, + 0xb913179899f68584,0x28e2557b59846e3f, + 0xe757dd7ec07426e5,0x331aeada2fe589cf, + 0x9096ea6f3848984f,0x3ff0d2c85def7621, + 0xb4bca50b065abe63,0xfed077a756b53a9, + 0xe1ebce4dc7f16dfb,0xd3e8495912c62894, + 0x8d3360f09cf6e4bd,0x64712dd7abbbd95c, + 0xb080392cc4349dec,0xbd8d794d96aacfb3, + 0xdca04777f541c567,0xecf0d7a0fc5583a0, + 0x89e42caaf9491b60,0xf41686c49db57244, + 0xac5d37d5b79b6239,0x311c2875c522ced5, + 0xd77485cb25823ac7,0x7d633293366b828b, + 0x86a8d39ef77164bc,0xae5dff9c02033197, + 0xa8530886b54dbdeb,0xd9f57f830283fdfc, + 0xd267caa862a12d66,0xd072df63c324fd7b, + 0x8380dea93da4bc60,0x4247cb9e59f71e6d, + 0xa46116538d0deb78,0x52d9be85f074e608, + 0xcd795be870516656,0x67902e276c921f8b, + 0x806bd9714632dff6,0xba1cd8a3db53b6, + 0xa086cfcd97bf97f3,0x80e8a40eccd228a4, + 0xc8a883c0fdaf7df0,0x6122cd128006b2cd, + 0xfad2a4b13d1b5d6c,0x796b805720085f81, + 0x9cc3a6eec6311a63,0xcbe3303674053bb0, + 0xc3f490aa77bd60fc,0xbedbfc4411068a9c, + 0xf4f1b4d515acb93b,0xee92fb5515482d44, + 0x991711052d8bf3c5,0x751bdd152d4d1c4a, + 0xbf5cd54678eef0b6,0xd262d45a78a0635d, + 0xef340a98172aace4,0x86fb897116c87c34, + 0x9580869f0e7aac0e,0xd45d35e6ae3d4da0, + 0xbae0a846d2195712,0x8974836059cca109, + 0xe998d258869facd7,0x2bd1a438703fc94b, + 0x91ff83775423cc06,0x7b6306a34627ddcf, + 0xb67f6455292cbf08,0x1a3bc84c17b1d542, + 0xe41f3d6a7377eeca,0x20caba5f1d9e4a93, + 0x8e938662882af53e,0x547eb47b7282ee9c, + 0xb23867fb2a35b28d,0xe99e619a4f23aa43, + 0xdec681f9f4c31f31,0x6405fa00e2ec94d4, + 0x8b3c113c38f9f37e,0xde83bc408dd3dd04, + 0xae0b158b4738705e,0x9624ab50b148d445, + 0xd98ddaee19068c76,0x3badd624dd9b0957, + 0x87f8a8d4cfa417c9,0xe54ca5d70a80e5d6, + 0xa9f6d30a038d1dbc,0x5e9fcf4ccd211f4c, + 0xd47487cc8470652b,0x7647c3200069671f, + 0x84c8d4dfd2c63f3b,0x29ecd9f40041e073, + 0xa5fb0a17c777cf09,0xf468107100525890, + 0xcf79cc9db955c2cc,0x7182148d4066eeb4, + 0x81ac1fe293d599bf,0xc6f14cd848405530, + 0xa21727db38cb002f,0xb8ada00e5a506a7c, + 0xca9cf1d206fdc03b,0xa6d90811f0e4851c, + 0xfd442e4688bd304a,0x908f4a166d1da663, + 0x9e4a9cec15763e2e,0x9a598e4e043287fe, + 0xc5dd44271ad3cdba,0x40eff1e1853f29fd, + 0xf7549530e188c128,0xd12bee59e68ef47c, + 0x9a94dd3e8cf578b9,0x82bb74f8301958ce, + 0xc13a148e3032d6e7,0xe36a52363c1faf01, + 0xf18899b1bc3f8ca1,0xdc44e6c3cb279ac1, + 0x96f5600f15a7b7e5,0x29ab103a5ef8c0b9, + 0xbcb2b812db11a5de,0x7415d448f6b6f0e7, + 0xebdf661791d60f56,0x111b495b3464ad21, + 0x936b9fcebb25c995,0xcab10dd900beec34, + 0xb84687c269ef3bfb,0x3d5d514f40eea742, + 0xe65829b3046b0afa,0xcb4a5a3112a5112, + 0x8ff71a0fe2c2e6dc,0x47f0e785eaba72ab, + 0xb3f4e093db73a093,0x59ed216765690f56, + 0xe0f218b8d25088b8,0x306869c13ec3532c, + 0x8c974f7383725573,0x1e414218c73a13fb, + 0xafbd2350644eeacf,0xe5d1929ef90898fa, + 0xdbac6c247d62a583,0xdf45f746b74abf39, + 0x894bc396ce5da772,0x6b8bba8c328eb783, + 0xab9eb47c81f5114f,0x66ea92f3f326564, + 0xd686619ba27255a2,0xc80a537b0efefebd, + 0x8613fd0145877585,0xbd06742ce95f5f36, + 0xa798fc4196e952e7,0x2c48113823b73704, + 0xd17f3b51fca3a7a0,0xf75a15862ca504c5, + 0x82ef85133de648c4,0x9a984d73dbe722fb, + 0xa3ab66580d5fdaf5,0xc13e60d0d2e0ebba, + 0xcc963fee10b7d1b3,0x318df905079926a8, + 0xffbbcfe994e5c61f,0xfdf17746497f7052, + 0x9fd561f1fd0f9bd3,0xfeb6ea8bedefa633, + 0xc7caba6e7c5382c8,0xfe64a52ee96b8fc0, + 0xf9bd690a1b68637b,0x3dfdce7aa3c673b0, + 0x9c1661a651213e2d,0x6bea10ca65c084e, + 0xc31bfa0fe5698db8,0x486e494fcff30a62, + 0xf3e2f893dec3f126,0x5a89dba3c3efccfa, + 0x986ddb5c6b3a76b7,0xf89629465a75e01c, + 0xbe89523386091465,0xf6bbb397f1135823, + 0xee2ba6c0678b597f,0x746aa07ded582e2c, + 0x94db483840b717ef,0xa8c2a44eb4571cdc, + 0xba121a4650e4ddeb,0x92f34d62616ce413, + 0xe896a0d7e51e1566,0x77b020baf9c81d17, + 0x915e2486ef32cd60,0xace1474dc1d122e, + 0xb5b5ada8aaff80b8,0xd819992132456ba, + 0xe3231912d5bf60e6,0x10e1fff697ed6c69, + 0x8df5efabc5979c8f,0xca8d3ffa1ef463c1, + 0xb1736b96b6fd83b3,0xbd308ff8a6b17cb2, + 0xddd0467c64bce4a0,0xac7cb3f6d05ddbde, + 0x8aa22c0dbef60ee4,0x6bcdf07a423aa96b, + 0xad4ab7112eb3929d,0x86c16c98d2c953c6, + 0xd89d64d57a607744,0xe871c7bf077ba8b7, + 0x87625f056c7c4a8b,0x11471cd764ad4972, + 0xa93af6c6c79b5d2d,0xd598e40d3dd89bcf, + 0xd389b47879823479,0x4aff1d108d4ec2c3, + 0x843610cb4bf160cb,0xcedf722a585139ba, + 0xa54394fe1eedb8fe,0xc2974eb4ee658828, + 0xce947a3da6a9273e,0x733d226229feea32, + 0x811ccc668829b887,0x806357d5a3f525f, + 0xa163ff802a3426a8,0xca07c2dcb0cf26f7, + 0xc9bcff6034c13052,0xfc89b393dd02f0b5, + 0xfc2c3f3841f17c67,0xbbac2078d443ace2, + 0x9d9ba7832936edc0,0xd54b944b84aa4c0d, + 0xc5029163f384a931,0xa9e795e65d4df11, + 0xf64335bcf065d37d,0x4d4617b5ff4a16d5, + 0x99ea0196163fa42e,0x504bced1bf8e4e45, + 0xc06481fb9bcf8d39,0xe45ec2862f71e1d6, + 0xf07da27a82c37088,0x5d767327bb4e5a4c, + 0x964e858c91ba2655,0x3a6a07f8d510f86f, + 0xbbe226efb628afea,0x890489f70a55368b, + 0xeadab0aba3b2dbe5,0x2b45ac74ccea842e, + 0x92c8ae6b464fc96f,0x3b0b8bc90012929d, + 0xb77ada0617e3bbcb,0x9ce6ebb40173744, + 0xe55990879ddcaabd,0xcc420a6a101d0515, + 0x8f57fa54c2a9eab6,0x9fa946824a12232d, + 0xb32df8e9f3546564,0x47939822dc96abf9, + 0xdff9772470297ebd,0x59787e2b93bc56f7, + 0x8bfbea76c619ef36,0x57eb4edb3c55b65a, + 0xaefae51477a06b03,0xede622920b6b23f1, + 0xdab99e59958885c4,0xe95fab368e45eced, + 0x88b402f7fd75539b,0x11dbcb0218ebb414, + 0xaae103b5fcd2a881,0xd652bdc29f26a119, + 0xd59944a37c0752a2,0x4be76d3346f0495f, + 0x857fcae62d8493a5,0x6f70a4400c562ddb, + 0xa6dfbd9fb8e5b88e,0xcb4ccd500f6bb952, + 0xd097ad07a71f26b2,0x7e2000a41346a7a7, + 0x825ecc24c873782f,0x8ed400668c0c28c8, + 0xa2f67f2dfa90563b,0x728900802f0f32fa, + 0xcbb41ef979346bca,0x4f2b40a03ad2ffb9, + 0xfea126b7d78186bc,0xe2f610c84987bfa8, + 0x9f24b832e6b0f436,0xdd9ca7d2df4d7c9, + 0xc6ede63fa05d3143,0x91503d1c79720dbb, + 0xf8a95fcf88747d94,0x75a44c6397ce912a, + 0x9b69dbe1b548ce7c,0xc986afbe3ee11aba, + 0xc24452da229b021b,0xfbe85badce996168, + 0xf2d56790ab41c2a2,0xfae27299423fb9c3, + 0x97c560ba6b0919a5,0xdccd879fc967d41a, + 0xbdb6b8e905cb600f,0x5400e987bbc1c920, + 0xed246723473e3813,0x290123e9aab23b68, + 0x9436c0760c86e30b,0xf9a0b6720aaf6521, + 0xb94470938fa89bce,0xf808e40e8d5b3e69, + 0xe7958cb87392c2c2,0xb60b1d1230b20e04, + 0x90bd77f3483bb9b9,0xb1c6f22b5e6f48c2, + 0xb4ecd5f01a4aa828,0x1e38aeb6360b1af3, + 0xe2280b6c20dd5232,0x25c6da63c38de1b0, + 0x8d590723948a535f,0x579c487e5a38ad0e, + 0xb0af48ec79ace837,0x2d835a9df0c6d851, + 0xdcdb1b2798182244,0xf8e431456cf88e65, + 0x8a08f0f8bf0f156b,0x1b8e9ecb641b58ff, + 0xac8b2d36eed2dac5,0xe272467e3d222f3f, + 0xd7adf884aa879177,0x5b0ed81dcc6abb0f, + 0x86ccbb52ea94baea,0x98e947129fc2b4e9, + 0xa87fea27a539e9a5,0x3f2398d747b36224, + 0xd29fe4b18e88640e,0x8eec7f0d19a03aad, + 0x83a3eeeef9153e89,0x1953cf68300424ac, + 0xa48ceaaab75a8e2b,0x5fa8c3423c052dd7, + 0xcdb02555653131b6,0x3792f412cb06794d, + 0x808e17555f3ebf11,0xe2bbd88bbee40bd0, + 0xa0b19d2ab70e6ed6,0x5b6aceaeae9d0ec4, + 0xc8de047564d20a8b,0xf245825a5a445275, + 0xfb158592be068d2e,0xeed6e2f0f0d56712, + 0x9ced737bb6c4183d,0x55464dd69685606b, + 0xc428d05aa4751e4c,0xaa97e14c3c26b886, + 0xf53304714d9265df,0xd53dd99f4b3066a8, + 0x993fe2c6d07b7fab,0xe546a8038efe4029, + 0xbf8fdb78849a5f96,0xde98520472bdd033, + 0xef73d256a5c0f77c,0x963e66858f6d4440, + 0x95a8637627989aad,0xdde7001379a44aa8, + 0xbb127c53b17ec159,0x5560c018580d5d52, + 0xe9d71b689dde71af,0xaab8f01e6e10b4a6, + 0x9226712162ab070d,0xcab3961304ca70e8, + 0xb6b00d69bb55c8d1,0x3d607b97c5fd0d22, + 0xe45c10c42a2b3b05,0x8cb89a7db77c506a, + 0x8eb98a7a9a5b04e3,0x77f3608e92adb242, + 0xb267ed1940f1c61c,0x55f038b237591ed3, + 0xdf01e85f912e37a3,0x6b6c46dec52f6688, + 0x8b61313bbabce2c6,0x2323ac4b3b3da015, + 0xae397d8aa96c1b77,0xabec975e0a0d081a, + 0xd9c7dced53c72255,0x96e7bd358c904a21, + 0x881cea14545c7575,0x7e50d64177da2e54, + 0xaa242499697392d2,0xdde50bd1d5d0b9e9, + 0xd4ad2dbfc3d07787,0x955e4ec64b44e864, + 0x84ec3c97da624ab4,0xbd5af13bef0b113e, + 0xa6274bbdd0fadd61,0xecb1ad8aeacdd58e, + 0xcfb11ead453994ba,0x67de18eda5814af2, + 0x81ceb32c4b43fcf4,0x80eacf948770ced7, + 0xa2425ff75e14fc31,0xa1258379a94d028d, + 0xcad2f7f5359a3b3e,0x96ee45813a04330, + 0xfd87b5f28300ca0d,0x8bca9d6e188853fc, + 0x9e74d1b791e07e48,0x775ea264cf55347e, + 0xc612062576589dda,0x95364afe032a819e, + 0xf79687aed3eec551,0x3a83ddbd83f52205, + 0x9abe14cd44753b52,0xc4926a9672793543, + 0xc16d9a0095928a27,0x75b7053c0f178294, + 0xf1c90080baf72cb1,0x5324c68b12dd6339, + 0x971da05074da7bee,0xd3f6fc16ebca5e04, + 0xbce5086492111aea,0x88f4bb1ca6bcf585, + 0xec1e4a7db69561a5,0x2b31e9e3d06c32e6, + 0x9392ee8e921d5d07,0x3aff322e62439fd0, + 0xb877aa3236a4b449,0x9befeb9fad487c3, + 0xe69594bec44de15b,0x4c2ebe687989a9b4, + 0x901d7cf73ab0acd9,0xf9d37014bf60a11, + 0xb424dc35095cd80f,0x538484c19ef38c95, + 0xe12e13424bb40e13,0x2865a5f206b06fba, + 0x8cbccc096f5088cb,0xf93f87b7442e45d4, + 0xafebff0bcb24aafe,0xf78f69a51539d749, + 0xdbe6fecebdedd5be,0xb573440e5a884d1c, + 0x89705f4136b4a597,0x31680a88f8953031, + 0xabcc77118461cefc,0xfdc20d2b36ba7c3e, + 0xd6bf94d5e57a42bc,0x3d32907604691b4d, + 0x8637bd05af6c69b5,0xa63f9a49c2c1b110, + 0xa7c5ac471b478423,0xfcf80dc33721d54, + 0xd1b71758e219652b,0xd3c36113404ea4a9, + 0x83126e978d4fdf3b,0x645a1cac083126ea, + 0xa3d70a3d70a3d70a,0x3d70a3d70a3d70a4, + 0xcccccccccccccccc,0xcccccccccccccccd, + 0x8000000000000000,0x0, + 0xa000000000000000,0x0, + 0xc800000000000000,0x0, + 0xfa00000000000000,0x0, + 0x9c40000000000000,0x0, + 0xc350000000000000,0x0, + 0xf424000000000000,0x0, + 0x9896800000000000,0x0, + 0xbebc200000000000,0x0, + 0xee6b280000000000,0x0, + 0x9502f90000000000,0x0, + 0xba43b74000000000,0x0, + 0xe8d4a51000000000,0x0, + 0x9184e72a00000000,0x0, + 0xb5e620f480000000,0x0, + 0xe35fa931a0000000,0x0, + 0x8e1bc9bf04000000,0x0, + 0xb1a2bc2ec5000000,0x0, + 0xde0b6b3a76400000,0x0, + 0x8ac7230489e80000,0x0, + 0xad78ebc5ac620000,0x0, + 0xd8d726b7177a8000,0x0, + 0x878678326eac9000,0x0, + 0xa968163f0a57b400,0x0, + 0xd3c21bcecceda100,0x0, + 0x84595161401484a0,0x0, + 0xa56fa5b99019a5c8,0x0, + 0xcecb8f27f4200f3a,0x0, + 0x813f3978f8940984,0x4000000000000000, + 0xa18f07d736b90be5,0x5000000000000000, + 0xc9f2c9cd04674ede,0xa400000000000000, + 0xfc6f7c4045812296,0x4d00000000000000, + 0x9dc5ada82b70b59d,0xf020000000000000, + 0xc5371912364ce305,0x6c28000000000000, + 0xf684df56c3e01bc6,0xc732000000000000, + 0x9a130b963a6c115c,0x3c7f400000000000, + 0xc097ce7bc90715b3,0x4b9f100000000000, + 0xf0bdc21abb48db20,0x1e86d40000000000, + 0x96769950b50d88f4,0x1314448000000000, + 0xbc143fa4e250eb31,0x17d955a000000000, + 0xeb194f8e1ae525fd,0x5dcfab0800000000, + 0x92efd1b8d0cf37be,0x5aa1cae500000000, + 0xb7abc627050305ad,0xf14a3d9e40000000, + 0xe596b7b0c643c719,0x6d9ccd05d0000000, + 0x8f7e32ce7bea5c6f,0xe4820023a2000000, + 0xb35dbf821ae4f38b,0xdda2802c8a800000, + 0xe0352f62a19e306e,0xd50b2037ad200000, + 0x8c213d9da502de45,0x4526f422cc340000, + 0xaf298d050e4395d6,0x9670b12b7f410000, + 0xdaf3f04651d47b4c,0x3c0cdd765f114000, + 0x88d8762bf324cd0f,0xa5880a69fb6ac800, + 0xab0e93b6efee0053,0x8eea0d047a457a00, + 0xd5d238a4abe98068,0x72a4904598d6d880, + 0x85a36366eb71f041,0x47a6da2b7f864750, + 0xa70c3c40a64e6c51,0x999090b65f67d924, + 0xd0cf4b50cfe20765,0xfff4b4e3f741cf6d, + 0x82818f1281ed449f,0xbff8f10e7a8921a4, + 0xa321f2d7226895c7,0xaff72d52192b6a0d, + 0xcbea6f8ceb02bb39,0x9bf4f8a69f764490, + 0xfee50b7025c36a08,0x2f236d04753d5b4, + 0x9f4f2726179a2245,0x1d762422c946590, + 0xc722f0ef9d80aad6,0x424d3ad2b7b97ef5, + 0xf8ebad2b84e0d58b,0xd2e0898765a7deb2, + 0x9b934c3b330c8577,0x63cc55f49f88eb2f, + 0xc2781f49ffcfa6d5,0x3cbf6b71c76b25fb, + 0xf316271c7fc3908a,0x8bef464e3945ef7a, + 0x97edd871cfda3a56,0x97758bf0e3cbb5ac, + 0xbde94e8e43d0c8ec,0x3d52eeed1cbea317, + 0xed63a231d4c4fb27,0x4ca7aaa863ee4bdd, + 0x945e455f24fb1cf8,0x8fe8caa93e74ef6a, + 0xb975d6b6ee39e436,0xb3e2fd538e122b44, + 0xe7d34c64a9c85d44,0x60dbbca87196b616, + 0x90e40fbeea1d3a4a,0xbc8955e946fe31cd, + 0xb51d13aea4a488dd,0x6babab6398bdbe41, + 0xe264589a4dcdab14,0xc696963c7eed2dd1, + 0x8d7eb76070a08aec,0xfc1e1de5cf543ca2, + 0xb0de65388cc8ada8,0x3b25a55f43294bcb, + 0xdd15fe86affad912,0x49ef0eb713f39ebe, + 0x8a2dbf142dfcc7ab,0x6e3569326c784337, + 0xacb92ed9397bf996,0x49c2c37f07965404, + 0xd7e77a8f87daf7fb,0xdc33745ec97be906, + 0x86f0ac99b4e8dafd,0x69a028bb3ded71a3, + 0xa8acd7c0222311bc,0xc40832ea0d68ce0c, + 0xd2d80db02aabd62b,0xf50a3fa490c30190, + 0x83c7088e1aab65db,0x792667c6da79e0fa, + 0xa4b8cab1a1563f52,0x577001b891185938, + 0xcde6fd5e09abcf26,0xed4c0226b55e6f86, + 0x80b05e5ac60b6178,0x544f8158315b05b4, + 0xa0dc75f1778e39d6,0x696361ae3db1c721, + 0xc913936dd571c84c,0x3bc3a19cd1e38e9, + 0xfb5878494ace3a5f,0x4ab48a04065c723, + 0x9d174b2dcec0e47b,0x62eb0d64283f9c76, + 0xc45d1df942711d9a,0x3ba5d0bd324f8394, + 0xf5746577930d6500,0xca8f44ec7ee36479, + 0x9968bf6abbe85f20,0x7e998b13cf4e1ecb, + 0xbfc2ef456ae276e8,0x9e3fedd8c321a67e, + 0xefb3ab16c59b14a2,0xc5cfe94ef3ea101e, + 0x95d04aee3b80ece5,0xbba1f1d158724a12, + 0xbb445da9ca61281f,0x2a8a6e45ae8edc97, + 0xea1575143cf97226,0xf52d09d71a3293bd, + 0x924d692ca61be758,0x593c2626705f9c56, + 0xb6e0c377cfa2e12e,0x6f8b2fb00c77836c, + 0xe498f455c38b997a,0xb6dfb9c0f956447, + 0x8edf98b59a373fec,0x4724bd4189bd5eac, + 0xb2977ee300c50fe7,0x58edec91ec2cb657, + 0xdf3d5e9bc0f653e1,0x2f2967b66737e3ed, + 0x8b865b215899f46c,0xbd79e0d20082ee74, + 0xae67f1e9aec07187,0xecd8590680a3aa11, + 0xda01ee641a708de9,0xe80e6f4820cc9495, + 0x884134fe908658b2,0x3109058d147fdcdd, + 0xaa51823e34a7eede,0xbd4b46f0599fd415, + 0xd4e5e2cdc1d1ea96,0x6c9e18ac7007c91a, + 0x850fadc09923329e,0x3e2cf6bc604ddb0, + 0xa6539930bf6bff45,0x84db8346b786151c, + 0xcfe87f7cef46ff16,0xe612641865679a63, + 0x81f14fae158c5f6e,0x4fcb7e8f3f60c07e, + 0xa26da3999aef7749,0xe3be5e330f38f09d, + 0xcb090c8001ab551c,0x5cadf5bfd3072cc5, + 0xfdcb4fa002162a63,0x73d9732fc7c8f7f6, + 0x9e9f11c4014dda7e,0x2867e7fddcdd9afa, + 0xc646d63501a1511d,0xb281e1fd541501b8, + 0xf7d88bc24209a565,0x1f225a7ca91a4226, + 0x9ae757596946075f,0x3375788de9b06958, + 0xc1a12d2fc3978937,0x52d6b1641c83ae, + 0xf209787bb47d6b84,0xc0678c5dbd23a49a, + 0x9745eb4d50ce6332,0xf840b7ba963646e0, + 0xbd176620a501fbff,0xb650e5a93bc3d898, + 0xec5d3fa8ce427aff,0xa3e51f138ab4cebe, + 0x93ba47c980e98cdf,0xc66f336c36b10137, + 0xb8a8d9bbe123f017,0xb80b0047445d4184, + 0xe6d3102ad96cec1d,0xa60dc059157491e5, + 0x9043ea1ac7e41392,0x87c89837ad68db2f, + 0xb454e4a179dd1877,0x29babe4598c311fb, + 0xe16a1dc9d8545e94,0xf4296dd6fef3d67a, + 0x8ce2529e2734bb1d,0x1899e4a65f58660c, + 0xb01ae745b101e9e4,0x5ec05dcff72e7f8f, + 0xdc21a1171d42645d,0x76707543f4fa1f73, + 0x899504ae72497eba,0x6a06494a791c53a8, + 0xabfa45da0edbde69,0x487db9d17636892, + 0xd6f8d7509292d603,0x45a9d2845d3c42b6, + 0x865b86925b9bc5c2,0xb8a2392ba45a9b2, + 0xa7f26836f282b732,0x8e6cac7768d7141e, + 0xd1ef0244af2364ff,0x3207d795430cd926, + 0x8335616aed761f1f,0x7f44e6bd49e807b8, + 0xa402b9c5a8d3a6e7,0x5f16206c9c6209a6, + 0xcd036837130890a1,0x36dba887c37a8c0f, + 0x802221226be55a64,0xc2494954da2c9789, + 0xa02aa96b06deb0fd,0xf2db9baa10b7bd6c, + 0xc83553c5c8965d3d,0x6f92829494e5acc7, + 0xfa42a8b73abbf48c,0xcb772339ba1f17f9, + 0x9c69a97284b578d7,0xff2a760414536efb, + 0xc38413cf25e2d70d,0xfef5138519684aba, + 0xf46518c2ef5b8cd1,0x7eb258665fc25d69, + 0x98bf2f79d5993802,0xef2f773ffbd97a61, + 0xbeeefb584aff8603,0xaafb550ffacfd8fa, + 0xeeaaba2e5dbf6784,0x95ba2a53f983cf38, + 0x952ab45cfa97a0b2,0xdd945a747bf26183, + 0xba756174393d88df,0x94f971119aeef9e4, + 0xe912b9d1478ceb17,0x7a37cd5601aab85d, + 0x91abb422ccb812ee,0xac62e055c10ab33a, + 0xb616a12b7fe617aa,0x577b986b314d6009, + 0xe39c49765fdf9d94,0xed5a7e85fda0b80b, + 0x8e41ade9fbebc27d,0x14588f13be847307, + 0xb1d219647ae6b31c,0x596eb2d8ae258fc8, + 0xde469fbd99a05fe3,0x6fca5f8ed9aef3bb, + 0x8aec23d680043bee,0x25de7bb9480d5854, + 0xada72ccc20054ae9,0xaf561aa79a10ae6a, + 0xd910f7ff28069da4,0x1b2ba1518094da04, + 0x87aa9aff79042286,0x90fb44d2f05d0842, + 0xa99541bf57452b28,0x353a1607ac744a53, + 0xd3fa922f2d1675f2,0x42889b8997915ce8, + 0x847c9b5d7c2e09b7,0x69956135febada11, + 0xa59bc234db398c25,0x43fab9837e699095, + 0xcf02b2c21207ef2e,0x94f967e45e03f4bb, + 0x8161afb94b44f57d,0x1d1be0eebac278f5, + 0xa1ba1ba79e1632dc,0x6462d92a69731732, + 0xca28a291859bbf93,0x7d7b8f7503cfdcfe, + 0xfcb2cb35e702af78,0x5cda735244c3d43e, + 0x9defbf01b061adab,0x3a0888136afa64a7, + 0xc56baec21c7a1916,0x88aaa1845b8fdd0, + 0xf6c69a72a3989f5b,0x8aad549e57273d45, + 0x9a3c2087a63f6399,0x36ac54e2f678864b, + 0xc0cb28a98fcf3c7f,0x84576a1bb416a7dd, + 0xf0fdf2d3f3c30b9f,0x656d44a2a11c51d5, + 0x969eb7c47859e743,0x9f644ae5a4b1b325, + 0xbc4665b596706114,0x873d5d9f0dde1fee, + 0xeb57ff22fc0c7959,0xa90cb506d155a7ea, + 0x9316ff75dd87cbd8,0x9a7f12442d588f2, + 0xb7dcbf5354e9bece,0xc11ed6d538aeb2f, + 0xe5d3ef282a242e81,0x8f1668c8a86da5fa, + 0x8fa475791a569d10,0xf96e017d694487bc, + 0xb38d92d760ec4455,0x37c981dcc395a9ac, + 0xe070f78d3927556a,0x85bbe253f47b1417, + 0x8c469ab843b89562,0x93956d7478ccec8e, + 0xaf58416654a6babb,0x387ac8d1970027b2, + 0xdb2e51bfe9d0696a,0x6997b05fcc0319e, + 0x88fcf317f22241e2,0x441fece3bdf81f03, + 0xab3c2fddeeaad25a,0xd527e81cad7626c3, + 0xd60b3bd56a5586f1,0x8a71e223d8d3b074, + 0x85c7056562757456,0xf6872d5667844e49, + 0xa738c6bebb12d16c,0xb428f8ac016561db, + 0xd106f86e69d785c7,0xe13336d701beba52, + 0x82a45b450226b39c,0xecc0024661173473, + 0xa34d721642b06084,0x27f002d7f95d0190, + 0xcc20ce9bd35c78a5,0x31ec038df7b441f4, + 0xff290242c83396ce,0x7e67047175a15271, + 0x9f79a169bd203e41,0xf0062c6e984d386, + 0xc75809c42c684dd1,0x52c07b78a3e60868, + 0xf92e0c3537826145,0xa7709a56ccdf8a82, + 0x9bbcc7a142b17ccb,0x88a66076400bb691, + 0xc2abf989935ddbfe,0x6acff893d00ea435, + 0xf356f7ebf83552fe,0x583f6b8c4124d43, + 0x98165af37b2153de,0xc3727a337a8b704a, + 0xbe1bf1b059e9a8d6,0x744f18c0592e4c5c, + 0xeda2ee1c7064130c,0x1162def06f79df73, + 0x9485d4d1c63e8be7,0x8addcb5645ac2ba8, + 0xb9a74a0637ce2ee1,0x6d953e2bd7173692, + 0xe8111c87c5c1ba99,0xc8fa8db6ccdd0437, + 0x910ab1d4db9914a0,0x1d9c9892400a22a2, + 0xb54d5e4a127f59c8,0x2503beb6d00cab4b, + 0xe2a0b5dc971f303a,0x2e44ae64840fd61d, + 0x8da471a9de737e24,0x5ceaecfed289e5d2, + 0xb10d8e1456105dad,0x7425a83e872c5f47, + 0xdd50f1996b947518,0xd12f124e28f77719, + 0x8a5296ffe33cc92f,0x82bd6b70d99aaa6f, + 0xace73cbfdc0bfb7b,0x636cc64d1001550b, + 0xd8210befd30efa5a,0x3c47f7e05401aa4e, + 0x8714a775e3e95c78,0x65acfaec34810a71, + 0xa8d9d1535ce3b396,0x7f1839a741a14d0d, + 0xd31045a8341ca07c,0x1ede48111209a050, + 0x83ea2b892091e44d,0x934aed0aab460432, + 0xa4e4b66b68b65d60,0xf81da84d5617853f, + 0xce1de40642e3f4b9,0x36251260ab9d668e, + 0x80d2ae83e9ce78f3,0xc1d72b7c6b426019, + 0xa1075a24e4421730,0xb24cf65b8612f81f, + 0xc94930ae1d529cfc,0xdee033f26797b627, + 0xfb9b7cd9a4a7443c,0x169840ef017da3b1, + 0x9d412e0806e88aa5,0x8e1f289560ee864e, + 0xc491798a08a2ad4e,0xf1a6f2bab92a27e2, + 0xf5b5d7ec8acb58a2,0xae10af696774b1db, + 0x9991a6f3d6bf1765,0xacca6da1e0a8ef29, + 0xbff610b0cc6edd3f,0x17fd090a58d32af3, + 0xeff394dcff8a948e,0xddfc4b4cef07f5b0, + 0x95f83d0a1fb69cd9,0x4abdaf101564f98e, + 0xbb764c4ca7a4440f,0x9d6d1ad41abe37f1, + 0xea53df5fd18d5513,0x84c86189216dc5ed, + 0x92746b9be2f8552c,0x32fd3cf5b4e49bb4, + 0xb7118682dbb66a77,0x3fbc8c33221dc2a1, + 0xe4d5e82392a40515,0xfabaf3feaa5334a, + 0x8f05b1163ba6832d,0x29cb4d87f2a7400e, + 0xb2c71d5bca9023f8,0x743e20e9ef511012, + 0xdf78e4b2bd342cf6,0x914da9246b255416, + 0x8bab8eefb6409c1a,0x1ad089b6c2f7548e, + 0xae9672aba3d0c320,0xa184ac2473b529b1, + 0xda3c0f568cc4f3e8,0xc9e5d72d90a2741e, + 0x8865899617fb1871,0x7e2fa67c7a658892, + 0xaa7eebfb9df9de8d,0xddbb901b98feeab7, + 0xd51ea6fa85785631,0x552a74227f3ea565, + 0x8533285c936b35de,0xd53a88958f87275f, + 0xa67ff273b8460356,0x8a892abaf368f137, + 0xd01fef10a657842c,0x2d2b7569b0432d85, + 0x8213f56a67f6b29b,0x9c3b29620e29fc73, + 0xa298f2c501f45f42,0x8349f3ba91b47b8f, + 0xcb3f2f7642717713,0x241c70a936219a73, + 0xfe0efb53d30dd4d7,0xed238cd383aa0110, + 0x9ec95d1463e8a506,0xf4363804324a40aa, + 0xc67bb4597ce2ce48,0xb143c6053edcd0d5, + 0xf81aa16fdc1b81da,0xdd94b7868e94050a, + 0x9b10a4e5e9913128,0xca7cf2b4191c8326, + 0xc1d4ce1f63f57d72,0xfd1c2f611f63a3f0, + 0xf24a01a73cf2dccf,0xbc633b39673c8cec, + 0x976e41088617ca01,0xd5be0503e085d813, + 0xbd49d14aa79dbc82,0x4b2d8644d8a74e18, + 0xec9c459d51852ba2,0xddf8e7d60ed1219e, + 0x93e1ab8252f33b45,0xcabb90e5c942b503, + 0xb8da1662e7b00a17,0x3d6a751f3b936243, + 0xe7109bfba19c0c9d,0xcc512670a783ad4, + 0x906a617d450187e2,0x27fb2b80668b24c5, + 0xb484f9dc9641e9da,0xb1f9f660802dedf6, + 0xe1a63853bbd26451,0x5e7873f8a0396973, + 0x8d07e33455637eb2,0xdb0b487b6423e1e8, + 0xb049dc016abc5e5f,0x91ce1a9a3d2cda62, + 0xdc5c5301c56b75f7,0x7641a140cc7810fb, + 0x89b9b3e11b6329ba,0xa9e904c87fcb0a9d, + 0xac2820d9623bf429,0x546345fa9fbdcd44, + 0xd732290fbacaf133,0xa97c177947ad4095, + 0x867f59a9d4bed6c0,0x49ed8eabcccc485d, + 0xa81f301449ee8c70,0x5c68f256bfff5a74, + 0xd226fc195c6a2f8c,0x73832eec6fff3111, + 0x83585d8fd9c25db7,0xc831fd53c5ff7eab, + 0xa42e74f3d032f525,0xba3e7ca8b77f5e55, + 0xcd3a1230c43fb26f,0x28ce1bd2e55f35eb, + 0x80444b5e7aa7cf85,0x7980d163cf5b81b3, + 0xa0555e361951c366,0xd7e105bcc332621f, + 0xc86ab5c39fa63440,0x8dd9472bf3fefaa7, + 0xfa856334878fc150,0xb14f98f6f0feb951, + 0x9c935e00d4b9d8d2,0x6ed1bf9a569f33d3, + 0xc3b8358109e84f07,0xa862f80ec4700c8, + 0xf4a642e14c6262c8,0xcd27bb612758c0fa, + 0x98e7e9cccfbd7dbd,0x8038d51cb897789c, + 0xbf21e44003acdd2c,0xe0470a63e6bd56c3, + 0xeeea5d5004981478,0x1858ccfce06cac74, + 0x95527a5202df0ccb,0xf37801e0c43ebc8, + 0xbaa718e68396cffd,0xd30560258f54e6ba, + 0xe950df20247c83fd,0x47c6b82ef32a2069, + 0x91d28b7416cdd27e,0x4cdc331d57fa5441, + 0xb6472e511c81471d,0xe0133fe4adf8e952, + 0xe3d8f9e563a198e5,0x58180fddd97723a6, + 0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,}; +using powers = powers_template<>; + +} + +#endif + + +#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H +#define FASTFLOAT_DECIMAL_TO_BINARY_H + +#include <cfloat> +#include <cinttypes> +#include <cmath> +#include <cstdint> +#include <cstdlib> +#include <cstring> + +namespace fast_float { + +// This will compute or rather approximate w * 5**q and return a pair of 64-bit words approximating +// the result, with the "high" part corresponding to the most significant bits and the +// low part corresponding to the least significant bits. +// +template <int bit_precision> +fastfloat_really_inline +value128 compute_product_approximation(int64_t q, uint64_t w) { + const int index = 2 * int(q - powers::smallest_power_of_five); + // For small values of q, e.g., q in [0,27], the answer is always exact because + // The line value128 firstproduct = full_multiplication(w, power_of_five_128[index]); + // gives the exact answer. + value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]); + static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]"); + constexpr uint64_t precision_mask = (bit_precision < 64) ? + (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) + : uint64_t(0xFFFFFFFFFFFFFFFF); + if((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower) + // regarding the second product, we only need secondproduct.high, but our expectation is that the compiler will optimize this extra work away if needed. + value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]); + firstproduct.low += secondproduct.high; + if(secondproduct.high > firstproduct.low) { + firstproduct.high++; + } + } + return firstproduct; +} + +namespace detail { +/** + * For q in (0,350), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * floor(p) + q + * where + * p = log(5**q)/log(2) = q * log(5)/log(2) + * + * For negative values of q in (-400,0), we have that + * f = (((152170 + 65536) * q ) >> 16); + * is equal to + * -ceil(p) + q + * where + * p = log(5**-q)/log(2) = -q * log(5)/log(2) + */ + constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { + return (((152170 + 65536) * q) >> 16) + 63; + } +} // namespace detail + +// create an adjusted mantissa, biased by the invalid power2 +// for significant digits already multiplied by 10 ** q. +template <typename binary> +fastfloat_really_inline +adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { + int hilz = int(w >> 63) ^ 1; + adjusted_mantissa answer; + answer.mantissa = w << hilz; + int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); + answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias); + return answer; +} + +// w * 10 ** q, without rounding the representation up. +// the power2 in the exponent will be adjusted by invalid_am_bias. +template <typename binary> +fastfloat_really_inline +adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept { + int lz = leading_zeroes(w); + w <<= lz; + value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); + return compute_error_scaled<binary>(q, product.high, lz); +} + +// w * 10 ** q +// The returned value should be a valid ieee64 number that simply need to be packed. +// However, in some very rare cases, the computation will fail. In such cases, we +// return an adjusted_mantissa with a negative power of 2: the caller should recompute +// in such cases. +template <typename binary> +fastfloat_really_inline +adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { + adjusted_mantissa answer; + if ((w == 0) || (q < binary::smallest_power_of_ten())) { + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + if (q > binary::largest_power_of_ten()) { + // we want to get infinity: + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + return answer; + } + // At this point in time q is in [powers::smallest_power_of_five, powers::largest_power_of_five]. + + // We want the most significant bit of i to be 1. Shift if needed. + int lz = leading_zeroes(w); + w <<= lz; + + // The required precision is binary::mantissa_explicit_bits() + 3 because + // 1. We need the implicit bit + // 2. We need an extra bit for rounding purposes + // 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift) + + value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); + if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further + // In some very rare cases, this could happen, in which case we might need a more accurate + // computation that what we can provide cheaply. This is very, very unlikely. + // + const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0, + // and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation. + if(!inside_safe_exponent) { + return compute_error_scaled<binary>(q, product.high, lz); + } + } + // The "compute_product_approximation" function can be slightly slower than a branchless approach: + // value128 product = compute_product(q, w); + // but in practice, we can win big with the compute_product_approximation if its additional branch + // is easily predicted. Which is best is data specific. + int upperbit = int(product.high >> 63); + + answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); + + answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent()); + if (answer.power2 <= 0) { // we have a subnormal? + // Here have that answer.power2 <= 0 so -answer.power2 >= 0 + if(-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you have a zero for sure. + answer.power2 = 0; + answer.mantissa = 0; + // result should be zero + return answer; + } + // next line is safe because -answer.power2 + 1 < 64 + answer.mantissa >>= -answer.power2 + 1; + // Thankfully, we can't have both "round-to-even" and subnormals because + // "round-to-even" only occurs for powers close to 0. + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + // There is a weird scenario where we don't have a subnormal but just. + // Suppose we start with 2.2250738585072013e-308, we end up + // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal + // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round + // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer + // subnormal, but we can only know this after rounding. + // So we only declare a subnormal if we are smaller than the threshold. + answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1; + return answer; + } + + // usually, we round *up*, but if we fall right in between and and we have an + // even basis, we need to round down + // We are only concerned with the cases where 5**q fits in single 64-bit word. + if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) && + ((answer.mantissa & 3) == 1) ) { // we may fall between two floats! + // To be in-between two floats we need that in doing + // answer.mantissa = product.high >> (upperbit + 64 - binary::mantissa_explicit_bits() - 3); + // ... we dropped out only zeroes. But if this happened, then we can go back!!! + if((answer.mantissa << (upperbit + 64 - binary::mantissa_explicit_bits() - 3)) == product.high) { + answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up + } + } + + answer.mantissa += (answer.mantissa & 1); // round up + answer.mantissa >>= 1; + if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) { + answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); + answer.power2++; // undo previous addition + } + + answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); + if (answer.power2 >= binary::infinite_power()) { // infinity + answer.power2 = binary::infinite_power(); + answer.mantissa = 0; + } + return answer; +} + +} // namespace fast_float + +#endif + + +#ifndef FASTFLOAT_BIGINT_H +#define FASTFLOAT_BIGINT_H + +#include <algorithm> +#include <cstdint> +#include <climits> +#include <cstring> + + +namespace fast_float { + +// the limb width: we want efficient multiplication of double the bits in +// limb, or for 64-bit limbs, at least 64-bit multiplication where we can +// extract the high and low parts efficiently. this is every 64-bit +// architecture except for sparc, which emulates 128-bit multiplication. +// we might have platforms where `CHAR_BIT` is not 8, so let's avoid +// doing `8 * sizeof(limb)`. +#if defined(FASTFLOAT_64BIT) && !defined(__sparc) +#define FASTFLOAT_64BIT_LIMB +typedef uint64_t limb; +constexpr size_t limb_bits = 64; +#else +#define FASTFLOAT_32BIT_LIMB +typedef uint32_t limb; +constexpr size_t limb_bits = 32; +#endif + +typedef span<limb> limb_span; + +// number of bits in a bigint. this needs to be at least the number +// of bits required to store the largest bigint, which is +// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or +// ~3600 bits, so we round to 4000. +constexpr size_t bigint_bits = 4000; +constexpr size_t bigint_limbs = bigint_bits / limb_bits; + +// vector-like type that is allocated on the stack. the entire +// buffer is pre-allocated, and only the length changes. +template <uint16_t size> +struct stackvec { + limb data[size]; + // we never need more than 150 limbs + uint16_t length{0}; + + stackvec() = default; + stackvec(const stackvec &) = delete; + stackvec &operator=(const stackvec &) = delete; + stackvec(stackvec &&) = delete; + stackvec &operator=(stackvec &&other) = delete; + + // create stack vector from existing limb span. + stackvec(limb_span s) { + FASTFLOAT_ASSERT(try_extend(s)); + } + + limb& operator[](size_t index) noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + const limb& operator[](size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + return data[index]; + } + // index from the end of the container + const limb& rindex(size_t index) const noexcept { + FASTFLOAT_DEBUG_ASSERT(index < length); + size_t rindex = length - index - 1; + return data[rindex]; + } + + // set the length, without bounds checking. + void set_len(size_t len) noexcept { + length = uint16_t(len); + } + constexpr size_t len() const noexcept { + return length; + } + constexpr bool is_empty() const noexcept { + return length == 0; + } + constexpr size_t capacity() const noexcept { + return size; + } + // append item to vector, without bounds checking + void push_unchecked(limb value) noexcept { + data[length] = value; + length++; + } + // append item to vector, returning if item was added + bool try_push(limb value) noexcept { + if (len() < capacity()) { + push_unchecked(value); + return true; + } else { + return false; + } + } + // add items to the vector, from a span, without bounds checking + void extend_unchecked(limb_span s) noexcept { + limb* ptr = data + length; + ::memcpy((void*)ptr, (const void*)s.ptr, sizeof(limb) * s.len()); + set_len(len() + s.len()); + } + // try to add items to the vector, returning if items were added + bool try_extend(limb_span s) noexcept { + if (len() + s.len() <= capacity()) { + extend_unchecked(s); + return true; + } else { + return false; + } + } + // resize the vector, without bounds checking + // if the new size is longer than the vector, assign value to each + // appended item. + void resize_unchecked(size_t new_len, limb value) noexcept { + if (new_len > len()) { + size_t count = new_len - len(); + limb* first = data + len(); + limb* last = first + count; + ::std::fill(first, last, value); + set_len(new_len); + } else { + set_len(new_len); + } + } + // try to resize the vector, returning if the vector was resized. + bool try_resize(size_t new_len, limb value) noexcept { + if (new_len > capacity()) { + return false; + } else { + resize_unchecked(new_len, value); + return true; + } + } + // check if any limbs are non-zero after the given index. + // this needs to be done in reverse order, since the index + // is relative to the most significant limbs. + bool nonzero(size_t index) const noexcept { + while (index < len()) { + if (rindex(index) != 0) { + return true; + } + index++; + } + return false; + } + // normalize the big integer, so most-significant zero limbs are removed. + void normalize() noexcept { + while (len() > 0 && rindex(0) == 0) { + length--; + } + } +}; + +fastfloat_really_inline +uint64_t empty_hi64(bool& truncated) noexcept { + truncated = false; + return 0; +} + +fastfloat_really_inline +uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept { + truncated = false; + int shl = leading_zeroes(r0); + return r0 << shl; +} + +fastfloat_really_inline +uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept { + int shl = leading_zeroes(r0); + if (shl == 0) { + truncated = r1 != 0; + return r0; + } else { + int shr = 64 - shl; + truncated = (r1 << shl) != 0; + return (r0 << shl) | (r1 >> shr); + } +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept { + return uint64_hi64(r0, truncated); +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + return uint64_hi64((x0 << 32) | x1, truncated); +} + +fastfloat_really_inline +uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept { + uint64_t x0 = r0; + uint64_t x1 = r1; + uint64_t x2 = r2; + return uint64_hi64(x0, (x1 << 32) | x2, truncated); +} + +// add two small integers, checking for overflow. +// we want an efficient operation. for msvc, where +// we don't have built-in intrinsics, this is still +// pretty fast. +fastfloat_really_inline +limb scalar_add(limb x, limb y, bool& overflow) noexcept { + limb z; + +// gcc and clang +#if defined(__has_builtin) + #if __has_builtin(__builtin_add_overflow) + overflow = __builtin_add_overflow(x, y, &z); + return z; + #endif +#endif + + // generic, this still optimizes correctly on MSVC. + z = x + y; + overflow = z < x; + return z; +} + +// multiply two small integers, getting both the high and low bits. +fastfloat_really_inline +limb scalar_mul(limb x, limb y, limb& carry) noexcept { +#ifdef FASTFLOAT_64BIT_LIMB + #if defined(__SIZEOF_INT128__) + // GCC and clang both define it as an extension. + __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry); + carry = limb(z >> limb_bits); + return limb(z); + #else + // fallback, no native 128-bit integer multiplication with carry. + // on msvc, this optimizes identically, somehow. + value128 z = full_multiplication(x, y); + bool overflow; + z.low = scalar_add(z.low, carry, overflow); + z.high += uint64_t(overflow); // cannot overflow + carry = z.high; + return z.low; + #endif +#else + uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry); + carry = limb(z >> limb_bits); + return limb(z); +#endif +} + +// add scalar value to bigint starting from offset. +// used in grade school multiplication +template <uint16_t size> +inline bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept { + size_t index = start; + limb carry = y; + bool overflow; + while (carry != 0 && index < vec.len()) { + vec[index] = scalar_add(vec[index], carry, overflow); + carry = limb(overflow); + index += 1; + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add scalar value to bigint. +template <uint16_t size> +fastfloat_really_inline bool small_add(stackvec<size>& vec, limb y) noexcept { + return small_add_from(vec, y, 0); +} + +// multiply bigint by scalar value. +template <uint16_t size> +inline bool small_mul(stackvec<size>& vec, limb y) noexcept { + limb carry = 0; + for (size_t index = 0; index < vec.len(); index++) { + vec[index] = scalar_mul(vec[index], y, carry); + } + if (carry != 0) { + FASTFLOAT_TRY(vec.try_push(carry)); + } + return true; +} + +// add bigint to bigint starting from index. +// used in grade school multiplication +template <uint16_t size> +bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept { + // the effective x buffer is from `xstart..x.len()`, so exit early + // if we can't get that current range. + if (x.len() < start || y.len() > x.len() - start) { + FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); + } + + bool carry = false; + for (size_t index = 0; index < y.len(); index++) { + limb xi = x[index + start]; + limb yi = y[index]; + bool c1 = false; + bool c2 = false; + xi = scalar_add(xi, yi, c1); + if (carry) { + xi = scalar_add(xi, 1, c2); + } + x[index + start] = xi; + carry = c1 | c2; + } + + // handle overflow + if (carry) { + FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); + } + return true; +} + +// add bigint to bigint. +template <uint16_t size> +fastfloat_really_inline bool large_add_from(stackvec<size>& x, limb_span y) noexcept { + return large_add_from(x, y, 0); +} + +// grade-school multiplication algorithm +template <uint16_t size> +bool long_mul(stackvec<size>& x, limb_span y) noexcept { + limb_span xs = limb_span(x.data, x.len()); + stackvec<size> z(xs); + limb_span zs = limb_span(z.data, z.len()); + + if (y.len() != 0) { + limb y0 = y[0]; + FASTFLOAT_TRY(small_mul(x, y0)); + for (size_t index = 1; index < y.len(); index++) { + limb yi = y[index]; + stackvec<size> zi; + if (yi != 0) { + // re-use the same buffer throughout + zi.set_len(0); + FASTFLOAT_TRY(zi.try_extend(zs)); + FASTFLOAT_TRY(small_mul(zi, yi)); + limb_span zis = limb_span(zi.data, zi.len()); + FASTFLOAT_TRY(large_add_from(x, zis, index)); + } + } + } + + x.normalize(); + return true; +} + +// grade-school multiplication algorithm +template <uint16_t size> +bool large_mul(stackvec<size>& x, limb_span y) noexcept { + if (y.len() == 1) { + FASTFLOAT_TRY(small_mul(x, y[0])); + } else { + FASTFLOAT_TRY(long_mul(x, y)); + } + return true; +} + +// big integer type. implements a small subset of big integer +// arithmetic, using simple algorithms since asymptotically +// faster algorithms are slower for a small number of limbs. +// all operations assume the big-integer is normalized. +struct bigint { + // storage of the limbs, in little-endian order. + stackvec<bigint_limbs> vec; + + bigint(): vec() {} + bigint(const bigint &) = delete; + bigint &operator=(const bigint &) = delete; + bigint(bigint &&) = delete; + bigint &operator=(bigint &&other) = delete; + + bigint(uint64_t value): vec() { +#ifdef FASTFLOAT_64BIT_LIMB + vec.push_unchecked(value); +#else + vec.push_unchecked(uint32_t(value)); + vec.push_unchecked(uint32_t(value >> 32)); +#endif + vec.normalize(); + } + + // get the high 64 bits from the vector, and if bits were truncated. + // this is to get the significant digits for the float. + uint64_t hi64(bool& truncated) const noexcept { +#ifdef FASTFLOAT_64BIT_LIMB + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint64_hi64(vec.rindex(0), truncated); + } else { + uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated); + truncated |= vec.nonzero(2); + return result; + } +#else + if (vec.len() == 0) { + return empty_hi64(truncated); + } else if (vec.len() == 1) { + return uint32_hi64(vec.rindex(0), truncated); + } else if (vec.len() == 2) { + return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated); + } else { + uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated); + truncated |= vec.nonzero(3); + return result; + } +#endif + } + + // compare two big integers, returning the large value. + // assumes both are normalized. if the return value is + // negative, other is larger, if the return value is + // positive, this is larger, otherwise they are equal. + // the limbs are stored in little-endian order, so we + // must compare the limbs in ever order. + int compare(const bigint& other) const noexcept { + if (vec.len() > other.vec.len()) { + return 1; + } else if (vec.len() < other.vec.len()) { + return -1; + } else { + for (size_t index = vec.len(); index > 0; index--) { + limb xi = vec[index - 1]; + limb yi = other.vec[index - 1]; + if (xi > yi) { + return 1; + } else if (xi < yi) { + return -1; + } + } + return 0; + } + } + + // shift left each limb n bits, carrying over to the new limb + // returns true if we were able to shift all the digits. + bool shl_bits(size_t n) noexcept { + // Internally, for each item, we shift left by n, and add the previous + // right shifted limb-bits. + // For example, we transform (for u8) shifted left 2, to: + // b10100100 b01000010 + // b10 b10010001 b00001000 + FASTFLOAT_DEBUG_ASSERT(n != 0); + FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); + + size_t shl = n; + size_t shr = limb_bits - shl; + limb prev = 0; + for (size_t index = 0; index < vec.len(); index++) { + limb xi = vec[index]; + vec[index] = (xi << shl) | (prev >> shr); + prev = xi; + } + + limb carry = prev >> shr; + if (carry != 0) { + return vec.try_push(carry); + } + return true; + } + + // move the limbs left by `n` limbs. + bool shl_limbs(size_t n) noexcept { + FASTFLOAT_DEBUG_ASSERT(n != 0); + if (n + vec.len() > vec.capacity()) { + return false; + } else if (!vec.is_empty()) { + // move limbs + limb* dst = vec.data + n; + const limb* src = vec.data; + ::memmove(dst, src, sizeof(limb) * vec.len()); + // fill in empty limbs + limb* first = vec.data; + limb* last = first + n; + ::std::fill(first, last, 0); + vec.set_len(n + vec.len()); + return true; + } else { + return true; + } + } + + // move the limbs left by `n` bits. + bool shl(size_t n) noexcept { + size_t rem = n % limb_bits; + size_t div = n / limb_bits; + if (rem != 0) { + FASTFLOAT_TRY(shl_bits(rem)); + } + if (div != 0) { + FASTFLOAT_TRY(shl_limbs(div)); + } + return true; + } + + // get the number of leading zeros in the bigint. + int ctlz() const noexcept { + if (vec.is_empty()) { + return 0; + } else { +#ifdef FASTFLOAT_64BIT_LIMB + return leading_zeroes(vec.rindex(0)); +#else + // no use defining a specialized leading_zeroes for a 32-bit type. + uint64_t r0 = vec.rindex(0); + return leading_zeroes(r0 << 32); +#endif + } + } + + // get the number of bits in the bigint. + int bit_length() const noexcept { + int lz = ctlz(); + return int(limb_bits * vec.len()) - lz; + } + + bool mul(limb y) noexcept { + return small_mul(vec, y); + } + + bool add(limb y) noexcept { + return small_add(vec, y); + } + + // multiply as if by 2 raised to a power. + bool pow2(uint32_t exp) noexcept { + return shl(exp); + } + + // multiply as if by 5 raised to a power. + bool pow5(uint32_t exp) noexcept { + // multiply by a power of 5 + static constexpr uint32_t large_step = 135; + static constexpr uint64_t small_power_of_5[] = { + 1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL, + 1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL, + 6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL, + 3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL, + 2384185791015625UL, 11920928955078125UL, 59604644775390625UL, + 298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL, + }; +#ifdef FASTFLOAT_64BIT_LIMB + constexpr static limb large_power_of_5[] = { + 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL, + 10482974169319127550UL, 198276706040285095UL}; +#else + constexpr static limb large_power_of_5[] = { + 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U, + 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U}; +#endif + size_t large_length = sizeof(large_power_of_5) / sizeof(limb); + limb_span large = limb_span(large_power_of_5, large_length); + while (exp >= large_step) { + FASTFLOAT_TRY(large_mul(vec, large)); + exp -= large_step; + } +#ifdef FASTFLOAT_64BIT_LIMB + uint32_t small_step = 27; + limb max_native = 7450580596923828125UL; +#else + uint32_t small_step = 13; + limb max_native = 1220703125U; +#endif + while (exp >= small_step) { + FASTFLOAT_TRY(small_mul(vec, max_native)); + exp -= small_step; + } + if (exp != 0) { + FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp]))); + } + + return true; + } + + // multiply as if by 10 raised to a power. + bool pow10(uint32_t exp) noexcept { + FASTFLOAT_TRY(pow5(exp)); + return pow2(exp); + } +}; + +} // namespace fast_float + +#endif + + +#ifndef FASTFLOAT_ASCII_NUMBER_H +#define FASTFLOAT_ASCII_NUMBER_H + +#include <cctype> +#include <cstdint> +#include <cstring> +#include <iterator> + + +namespace fast_float { + +// Next function can be micro-optimized, but compilers are entirely +// able to optimize it well. +fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } + +fastfloat_really_inline uint64_t byteswap(uint64_t val) { + return (val & 0xFF00000000000000) >> 56 + | (val & 0x00FF000000000000) >> 40 + | (val & 0x0000FF0000000000) >> 24 + | (val & 0x000000FF00000000) >> 8 + | (val & 0x00000000FF000000) << 8 + | (val & 0x0000000000FF0000) << 24 + | (val & 0x000000000000FF00) << 40 + | (val & 0x00000000000000FF) << 56; +} + +fastfloat_really_inline uint64_t read_u64(const char *chars) { + uint64_t val; + ::memcpy(&val, chars, sizeof(uint64_t)); +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + return val; +} + +fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) { +#if FASTFLOAT_IS_BIG_ENDIAN == 1 + // Need to read as-if the number was in little-endian order. + val = byteswap(val); +#endif + ::memcpy(chars, &val, sizeof(uint64_t)); +} + +// credit @aqrit +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { + const uint64_t mask = 0x000000FF000000FF; + const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) + const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) + val -= 0x3030303030303030; + val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; + val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; + return uint32_t(val); +} + +fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept { + return parse_eight_digits_unrolled(read_u64(chars)); +} + +// credit @aqrit +fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { + return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & + 0x8080808080808080)); +} + +fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept { + return is_made_of_eight_digits_fast(read_u64(chars)); +} + +typedef span<const char> byte_span; + +struct parsed_number_string { + int64_t exponent{0}; + uint64_t mantissa{0}; + const char *lastmatch{nullptr}; + bool negative{false}; + bool valid{false}; + bool too_many_digits{false}; + // contains the range of the significant digits + byte_span integer{}; // non-nullable + byte_span fraction{}; // nullable +}; + +// Assuming that you use no more than 19 digits, this will +// parse an ASCII string. +fastfloat_really_inline +parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept { + const chars_format fmt = options.format; + const char decimal_point = options.decimal_point; + + parsed_number_string answer; + answer.valid = false; + answer.too_many_digits = false; + answer.negative = (*p == '-'); + if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here + ++p; + if (p == pend) { + return answer; + } + if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot + return answer; + } + } + const char *const start_digits = p; + + uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) + + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + // a multiplication by 10 is cheaper than an arbitrary integer + // multiplication + i = 10 * i + + uint64_t(*p - '0'); // might overflow, we will handle the overflow later + ++p; + } + const char *const end_of_integer_part = p; + int64_t digit_count = int64_t(end_of_integer_part - start_digits); + answer.integer = byte_span(start_digits, size_t(digit_count)); + int64_t exponent = 0; + if ((p != pend) && (*p == decimal_point)) { + ++p; + const char* before = p; + // can occur at most twice without overflowing, but let it occur more, since + // for integers with many digits, digit parsing is the primary bottleneck. + while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) { + i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok + p += 8; + } + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + ++p; + i = i * 10 + digit; // in rare cases, this will overflow, but that's ok + } + exponent = before - p; + answer.fraction = byte_span(before, size_t(p - before)); + digit_count -= exponent; + } + // we must have encountered at least one integer! + if (digit_count == 0) { + return answer; + } + int64_t exp_number = 0; // explicit exponential part + if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) { + const char * location_of_e = p; + ++p; + bool neg_exp = false; + if ((p != pend) && ('-' == *p)) { + neg_exp = true; + ++p; + } else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) + ++p; + } + if ((p == pend) || !is_integer(*p)) { + if(!(fmt & chars_format::fixed)) { + // We are in error. + return answer; + } + // Otherwise, we will be ignoring the 'e'. + p = location_of_e; + } else { + while ((p != pend) && is_integer(*p)) { + uint8_t digit = uint8_t(*p - '0'); + if (exp_number < 0x10000000) { + exp_number = 10 * exp_number + digit; + } + ++p; + } + if(neg_exp) { exp_number = - exp_number; } + exponent += exp_number; + } + } else { + // If it scientific and not fixed, we have to bail out. + if((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) { return answer; } + } + answer.lastmatch = p; + answer.valid = true; + + // If we frequently had to deal with long strings of digits, + // we could extend our code by using a 128-bit integer instead + // of a 64-bit integer. However, this is uncommon. + // + // We can deal with up to 19 digits. + if (digit_count > 19) { // this is uncommon + // It is possible that the integer had an overflow. + // We have to handle the case where we have 0.0000somenumber. + // We need to be mindful of the case where we only have zeroes... + // E.g., 0.000000000...000. + const char *start = start_digits; + while ((start != pend) && (*start == '0' || *start == decimal_point)) { + if(*start == '0') { digit_count --; } + start++; + } + if (digit_count > 19) { + answer.too_many_digits = true; + // Let us start again, this time, avoiding overflows. + // We don't need to check if is_integer, since we use the + // pre-tokenized spans from above. + i = 0; + p = answer.integer.ptr; + const char* int_end = p + answer.integer.len(); + const uint64_t minimal_nineteen_digit_integer{1000000000000000000}; + while((i < minimal_nineteen_digit_integer) && (p != int_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + if (i >= minimal_nineteen_digit_integer) { // We have a big integers + exponent = end_of_integer_part - p + exp_number; + } else { // We have a value with a fractional component. + p = answer.fraction.ptr; + const char* frac_end = p + answer.fraction.len(); + while((i < minimal_nineteen_digit_integer) && (p != frac_end)) { + i = i * 10 + uint64_t(*p - '0'); + ++p; + } + exponent = answer.fraction.ptr - p + exp_number; + } + // We have now corrected both exponent and i, to a truncated value + } + } + answer.exponent = exponent; + answer.mantissa = i; + return answer; +} + +} // namespace fast_float + +#endif + + +#ifndef FASTFLOAT_DIGIT_COMPARISON_H +#define FASTFLOAT_DIGIT_COMPARISON_H + +#include <algorithm> +#include <cstdint> +#include <cstring> +#include <iterator> + + +namespace fast_float { + +// 1e0 to 1e19 +constexpr static uint64_t powers_of_ten_uint64[] = { + 1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL, + 1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL, + 100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL, + 1000000000000000000UL, 10000000000000000000UL}; + +// calculate the exponent, in scientific notation, of the number. +// this algorithm is not even close to optimized, but it has no practical +// effect on performance: in order to have a faster algorithm, we'd need +// to slow down performance for faster algorithms, and this is still fast. +fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept { + uint64_t mantissa = num.mantissa; + int32_t exponent = int32_t(num.exponent); + while (mantissa >= 10000) { + mantissa /= 10000; + exponent += 4; + } + while (mantissa >= 100) { + mantissa /= 100; + exponent += 2; + } + while (mantissa >= 10) { + mantissa /= 10; + exponent += 1; + } + return exponent; +} + +// this converts a native floating-point number to an extended-precision float. +template <typename T> +fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { + adjusted_mantissa am; + int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent(); + if (std::is_same<T, float>::value) { + constexpr uint32_t exponent_mask = 0x7F800000; + constexpr uint32_t mantissa_mask = 0x007FFFFF; + constexpr uint64_t hidden_bit_mask = 0x00800000; + uint32_t bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; + } else { + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + } + } else { + constexpr uint64_t exponent_mask = 0x7FF0000000000000; + constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF; + constexpr uint64_t hidden_bit_mask = 0x0010000000000000; + uint64_t bits; + ::memcpy(&bits, &value, sizeof(T)); + if ((bits & exponent_mask) == 0) { + // denormal + am.power2 = 1 - bias; + am.mantissa = bits & mantissa_mask; + } else { + // normal + am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits()); + am.power2 -= bias; + am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; + } + } + + return am; +} + +// get the extended precision value of the halfway point between b and b+u. +// we are given a native float that represents b, so we need to adjust it +// halfway between b and b+u. +template <typename T> +fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept { + adjusted_mantissa am = to_extended(value); + am.mantissa <<= 1; + am.mantissa += 1; + am.power2 -= 1; + return am; +} + +// round an extended-precision float to the nearest machine float. +template <typename T, typename callback> +fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept { + int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; + if (-am.power2 >= mantissa_shift) { + // have a denormal float + int32_t shift = -am.power2 + 1; + cb(am, std::min(shift, 64)); + // check for round-up: if rounding-nearest carried us to the hidden bit. + am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1; + return; + } + + // have a normal float, use the default shift. + cb(am, mantissa_shift); + + // check for carry + if (am.mantissa >= (uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) { + am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); + am.power2++; + } + + // check for infinite: we could have carried to an infinite power + am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); + if (am.power2 >= binary_format<T>::infinite_power()) { + am.power2 = binary_format<T>::infinite_power(); + am.mantissa = 0; + } +} + +template <typename callback> +fastfloat_really_inline +void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept { + uint64_t mask; + uint64_t halfway; + if (shift == 64) { + mask = UINT64_MAX; + } else { + mask = (uint64_t(1) << shift) - 1; + } + if (shift == 0) { + halfway = 0; + } else { + halfway = uint64_t(1) << (shift - 1); + } + uint64_t truncated_bits = am.mantissa & mask; + uint64_t is_above = truncated_bits > halfway; + uint64_t is_halfway = truncated_bits == halfway; + + // shift digits into position + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; + + bool is_odd = (am.mantissa & 1) == 1; + am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); +} + +fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept { + if (shift == 64) { + am.mantissa = 0; + } else { + am.mantissa >>= shift; + } + am.power2 += shift; +} + +fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept { + uint64_t val; + while (std::distance(first, last) >= 8) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != 0x3030303030303030) { + break; + } + first += 8; + } + while (first != last) { + if (*first != '0') { + break; + } + first++; + } +} + +// determine if any non-zero digits were truncated. +// all characters must be valid digits. +fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept { + // do 8-bit optimizations, can just compare to 8 literal 0s. + uint64_t val; + while (std::distance(first, last) >= 8) { + ::memcpy(&val, first, sizeof(uint64_t)); + if (val != 0x3030303030303030) { + return true; + } + first += 8; + } + while (first != last) { + if (*first != '0') { + return true; + } + first++; + } + return false; +} + +fastfloat_really_inline bool is_truncated(byte_span s) noexcept { + return is_truncated(s.ptr, s.ptr + s.len()); +} + +fastfloat_really_inline +void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { + value = value * 100000000 + parse_eight_digits_unrolled(p); + p += 8; + counter += 8; + count += 8; +} + +fastfloat_really_inline +void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { + value = value * 10 + limb(*p - '0'); + p++; + counter++; + count++; +} + +fastfloat_really_inline +void add_native(bigint& big, limb power, limb value) noexcept { + big.mul(power); + big.add(value); +} + +fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcept { + // need to round-up the digits, but need to avoid rounding + // ....9999 to ...10000, which could cause a false halfway point. + add_native(big, 10, 1); + count++; +} + +// parse the significant digits into a big integer +inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept { + // try to minimize the number of big integer and scalar multiplication. + // therefore, try to parse 8 digits at a time, and multiply by the largest + // scalar value (9 or 19 digits) for each step. + size_t counter = 0; + digits = 0; + limb value = 0; +#ifdef FASTFLOAT_64BIT_LIMB + size_t step = 19; +#else + size_t step = 9; +#endif + + // process all integer digits. + const char* p = num.integer.ptr; + const char* pend = p + num.integer.len(); + skip_zeros(p, pend); + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (num.fraction.ptr != nullptr) { + truncated |= is_truncated(num.fraction); + } + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + + // add our fraction digits, if they're available. + if (num.fraction.ptr != nullptr) { + p = num.fraction.ptr; + pend = p + num.fraction.len(); + if (digits == 0) { + skip_zeros(p, pend); + } + // process all digits, in increments of step per loop + while (p != pend) { + while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { + parse_eight_digits(p, value, counter, digits); + } + while (counter < step && p != pend && digits < max_digits) { + parse_one_digit(p, value, counter, digits); + } + if (digits == max_digits) { + // add the temporary value, then check if we've truncated any digits + add_native(result, limb(powers_of_ten_uint64[counter]), value); + bool truncated = is_truncated(p, pend); + if (truncated) { + round_up_bigint(result, digits); + } + return; + } else { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + counter = 0; + value = 0; + } + } + } + + if (counter != 0) { + add_native(result, limb(powers_of_ten_uint64[counter]), value); + } +} + +template <typename T> +inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept { + FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); + adjusted_mantissa answer; + bool truncated; + answer.mantissa = bigmant.hi64(truncated); + int bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent(); + answer.power2 = bigmant.bit_length() - 64 + bias; + + round<T>(answer, [truncated](adjusted_mantissa& a, int32_t shift) { + round_nearest_tie_even(a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { + return is_above || (is_halfway && truncated) || (is_odd && is_halfway); + }); + }); + + return answer; +} + +// the scaling here is quite simple: we have, for the real digits `m * 10^e`, +// and for the theoretical digits `n * 2^f`. Since `e` is always negative, +// to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`. +// we then need to scale by `2^(f- e)`, and then the two significant digits +// are of the same magnitude. +template <typename T> +inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept { + bigint& real_digits = bigmant; + int32_t real_exp = exponent; + + // get the value of `b`, rounded down, and get a bigint representation of b+h + adjusted_mantissa am_b = am; + // gcc7 buf: use a lambda to remove the noexcept qualifier bug with -Wnoexcept-type. + round<T>(am_b, [](adjusted_mantissa&a, int32_t shift) { round_down(a, shift); }); + T b; + to_float(false, am_b, b); + adjusted_mantissa theor = to_extended_halfway(b); + bigint theor_digits(theor.mantissa); + int32_t theor_exp = theor.power2; + + // scale real digits and theor digits to be same power. + int32_t pow2_exp = theor_exp - real_exp; + uint32_t pow5_exp = uint32_t(-real_exp); + if (pow5_exp != 0) { + FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); + } + if (pow2_exp > 0) { + FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); + } else if (pow2_exp < 0) { + FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); + } + + // compare digits, and use it to director rounding + int ord = real_digits.compare(theor_digits); + adjusted_mantissa answer = am; + round<T>(answer, [ord](adjusted_mantissa& a, int32_t shift) { + round_nearest_tie_even(a, shift, [ord](bool is_odd, bool _, bool __) -> bool { + (void)_; // not needed, since we've done our comparison + (void)__; // not needed, since we've done our comparison + if (ord > 0) { + return true; + } else if (ord < 0) { + return false; + } else { + return is_odd; + } + }); + }); + + return answer; +} + +// parse the significant digits as a big integer to unambiguously round the +// the significant digits. here, we are trying to determine how to round +// an extended float representation close to `b+h`, halfway between `b` +// (the float rounded-down) and `b+u`, the next positive float. this +// algorithm is always correct, and uses one of two approaches. when +// the exponent is positive relative to the significant digits (such as +// 1234), we create a big-integer representation, get the high 64-bits, +// determine if any lower bits are truncated, and use that to direct +// rounding. in case of a negative exponent relative to the significant +// digits (such as 1.2345), we create a theoretical representation of +// `b` as a big-integer type, scaled to the same binary exponent as +// the actual digits. we then compare the big integer representations +// of both, and use that to direct rounding. +template <typename T> +inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept { + // remove the invalid exponent bias + am.power2 -= invalid_am_bias; + + int32_t sci_exp = scientific_exponent(num); + size_t max_digits = binary_format<T>::max_digits(); + size_t digits = 0; + bigint bigmant; + parse_mantissa(bigmant, num, max_digits, digits); + // can't underflow, since digits is at most max_digits. + int32_t exponent = sci_exp + 1 - int32_t(digits); + if (exponent >= 0) { + return positive_digit_comp<T>(bigmant, exponent); + } else { + return negative_digit_comp<T>(bigmant, am, exponent); + } +} + +} // namespace fast_float + +#endif + + +#ifndef FASTFLOAT_PARSE_NUMBER_H +#define FASTFLOAT_PARSE_NUMBER_H + + +#include <cmath> +#include <cstring> +#include <limits> +#include <system_error> + +namespace fast_float { + + +namespace detail { +/** + * Special case +inf, -inf, nan, infinity, -infinity. + * The case comparisons could be made much faster given that we know that the + * strings a null-free and fixed. + **/ +template <typename T> +from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept { + from_chars_result answer; + answer.ptr = first; + answer.ec = std::errc(); // be optimistic + bool minusSign = false; + if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here + minusSign = true; + ++first; + } + if (last - first >= 3) { + if (fastfloat_strncasecmp(first, "nan", 3)) { + answer.ptr = (first += 3); + value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN(); + // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). + if(first != last && *first == '(') { + for(const char* ptr = first + 1; ptr != last; ++ptr) { + if (*ptr == ')') { + answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) + break; + } + else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_')) + break; // forbidden char, not nan(n-char-seq-opt) + } + } + return answer; + } + if (fastfloat_strncasecmp(first, "inf", 3)) { + if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) { + answer.ptr = first + 8; + } else { + answer.ptr = first + 3; + } + value = minusSign ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity(); + return answer; + } + } + answer.ec = std::errc::invalid_argument; + return answer; +} + +} // namespace detail + +template<typename T> +from_chars_result from_chars(const char *first, const char *last, + T &value, chars_format fmt /*= chars_format::general*/) noexcept { + return from_chars_advanced(first, last, value, parse_options{fmt}); +} + +template<typename T> +from_chars_result from_chars_advanced(const char *first, const char *last, + T &value, parse_options options) noexcept { + + static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported"); + + + from_chars_result answer; + if (first == last) { + answer.ec = std::errc::invalid_argument; + answer.ptr = first; + return answer; + } + parsed_number_string pns = parse_number_string(first, last, options); + if (!pns.valid) { + return detail::parse_infnan(first, last, value); + } + answer.ec = std::errc(); // be optimistic + answer.ptr = pns.lastmatch; + // Next is Clinger's fast path. + if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) { + value = T(pns.mantissa); + if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); } + else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); } + if (pns.negative) { value = -value; } + return answer; + } + adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa); + if(pns.too_many_digits && am.power2 >= 0) { + if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) { + am = compute_error<binary_format<T>>(pns.exponent, pns.mantissa); + } + } + // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) and we have an invalid power (am.power2 < 0), + // then we need to go the long way around again. This is very uncommon. + if(am.power2 < 0) { am = digit_comp<T>(pns, am); } + to_float(pns.negative, am, value); + return answer; +} + +} // namespace fast_float + +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/rng/rng.hpp b/thirdparty/ryml/ext/c4core/src/c4/ext/rng/rng.hpp new file mode 100644 index 000000000..63af09008 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/rng/rng.hpp @@ -0,0 +1,192 @@ +/* Copyright (c) 2018 Arvid Gerstmann. + * + * https://arvid.io/2018/07/02/better-cxx-prng/ + * + * This code is licensed under MIT license. */ +#ifndef AG_RANDOM_H +#define AG_RANDOM_H + +#include <stdint.h> +#include <random> + + +namespace c4 { +namespace rng { + + +class splitmix +{ +public: + using result_type = uint32_t; + static constexpr result_type (min)() { return 0; } + static constexpr result_type (max)() { return UINT32_MAX; } + friend bool operator==(splitmix const &, splitmix const &); + friend bool operator!=(splitmix const &, splitmix const &); + + splitmix() : m_seed(1) {} + explicit splitmix(uint64_t s) : m_seed(s) {} + explicit splitmix(std::random_device &rd) + { + seed(rd); + } + + void seed(uint64_t s) { m_seed = s; } + void seed(std::random_device &rd) + { + m_seed = uint64_t(rd()) << 31 | uint64_t(rd()); + } + + result_type operator()() + { + uint64_t z = (m_seed += UINT64_C(0x9E3779B97F4A7C15)); + z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9); + z = (z ^ (z >> 27)) * UINT64_C(0x94D049BB133111EB); + return result_type((z ^ (z >> 31)) >> 31); + } + + void discard(unsigned long long n) + { + for (unsigned long long i = 0; i < n; ++i) + operator()(); + } + +private: + uint64_t m_seed; +}; + +inline bool operator==(splitmix const &lhs, splitmix const &rhs) +{ + return lhs.m_seed == rhs.m_seed; +} +inline bool operator!=(splitmix const &lhs, splitmix const &rhs) +{ + return lhs.m_seed != rhs.m_seed; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +class xorshift +{ +public: + using result_type = uint32_t; + static constexpr result_type (min)() { return 0; } + static constexpr result_type (max)() { return UINT32_MAX; } + friend bool operator==(xorshift const &, xorshift const &); + friend bool operator!=(xorshift const &, xorshift const &); + + xorshift() : m_seed(0xc1f651c67c62c6e0ull) {} + explicit xorshift(std::random_device &rd) + { + seed(rd); + } + + void seed(uint64_t s) { m_seed = s; } + void seed(std::random_device &rd) + { + m_seed = uint64_t(rd()) << 31 | uint64_t(rd()); + } + + result_type operator()() + { + uint64_t result = m_seed * 0xd989bcacc137dcd5ull; + m_seed ^= m_seed >> 11; + m_seed ^= m_seed << 31; + m_seed ^= m_seed >> 18; + return uint32_t(result >> 32ull); + } + + void discard(unsigned long long n) + { + for (unsigned long long i = 0; i < n; ++i) + operator()(); + } + +private: + uint64_t m_seed; +}; + +inline bool operator==(xorshift const &lhs, xorshift const &rhs) +{ + return lhs.m_seed == rhs.m_seed; +} +inline bool operator!=(xorshift const &lhs, xorshift const &rhs) +{ + return lhs.m_seed != rhs.m_seed; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +class pcg +{ +public: + using result_type = uint32_t; + static constexpr result_type (min)() { return 0; } + static constexpr result_type (max)() { return UINT32_MAX; } + friend bool operator==(pcg const &, pcg const &); + friend bool operator!=(pcg const &, pcg const &); + + pcg() + : m_state(0x853c49e6748fea9bULL) + , m_inc(0xda3e39cb94b95bdbULL) + {} + explicit pcg(uint64_t s) { m_state = s; m_inc = m_state << 1; } + explicit pcg(std::random_device &rd) + { + seed(rd); + } + + void seed(uint64_t s) { m_state = s; } + void seed(std::random_device &rd) + { + uint64_t s0 = uint64_t(rd()) << 31 | uint64_t(rd()); + uint64_t s1 = uint64_t(rd()) << 31 | uint64_t(rd()); + + m_state = 0; + m_inc = (s1 << 1) | 1; + (void)operator()(); + m_state += s0; + (void)operator()(); + } + + result_type operator()() + { + uint64_t oldstate = m_state; + m_state = oldstate * 6364136223846793005ULL + m_inc; + uint32_t xorshifted = uint32_t(((oldstate >> 18u) ^ oldstate) >> 27u); + //int rot = oldstate >> 59u; // the original. error? + int64_t rot = (int64_t)oldstate >> 59u; // error? + return (xorshifted >> rot) | (xorshifted << ((uint64_t)(-rot) & 31)); + } + + void discard(unsigned long long n) + { + for (unsigned long long i = 0; i < n; ++i) + operator()(); + } + +private: + uint64_t m_state; + uint64_t m_inc; +}; + +inline bool operator==(pcg const &lhs, pcg const &rhs) +{ + return lhs.m_state == rhs.m_state + && lhs.m_inc == rhs.m_inc; +} +inline bool operator!=(pcg const &lhs, pcg const &rhs) +{ + return lhs.m_state != rhs.m_state + || lhs.m_inc != rhs.m_inc; +} + +} // namespace rng +} // namespace c4 + +#endif /* AG_RANDOM_H */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/sg14/README.md b/thirdparty/ryml/ext/c4core/src/c4/ext/sg14/README.md new file mode 100644 index 000000000..11d0f94a0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/sg14/README.md @@ -0,0 +1 @@ +https://github.com/WG21-SG14/SG14/blob/master/SG14/inplace_function.h diff --git a/thirdparty/ryml/ext/c4core/src/c4/ext/sg14/inplace_function.h b/thirdparty/ryml/ext/c4core/src/c4/ext/sg14/inplace_function.h new file mode 100644 index 000000000..af3e83054 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/ext/sg14/inplace_function.h @@ -0,0 +1,347 @@ +/* + * 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. + */ +#ifndef _C4_EXT_SG14_INPLACE_FUNCTION_H_ +#define _C4_EXT_SG14_INPLACE_FUNCTION_H_ + +#include <type_traits> +#include <utility> +#include <functional> + +namespace stdext { + +namespace inplace_function_detail { + +static constexpr size_t InplaceFunctionDefaultCapacity = 32; + +#if defined(__GLIBCXX__) // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61458 +template<size_t Cap> +union aligned_storage_helper { + struct double1 { double a; }; + struct double4 { double a[4]; }; + template<class T> using maybe = typename std::conditional<(Cap >= sizeof(T)), T, char>::type; + char real_data[Cap]; + maybe<int> a; + maybe<long> b; + maybe<long long> c; + maybe<void*> d; + maybe<void(*)()> e; + maybe<double1> f; + maybe<double4> g; + maybe<long double> h; +}; + +template<size_t Cap, size_t Align = std::alignment_of<aligned_storage_helper<Cap>>::value> +struct aligned_storage { + using type = typename std::aligned_storage<Cap, Align>::type; +}; +#else +using std::aligned_storage; +#endif + +template<typename T> struct wrapper +{ + using type = T; +}; + +template<typename R, typename... Args> struct vtable +{ + using storage_ptr_t = void*; + + using invoke_ptr_t = R(*)(storage_ptr_t, Args&&...); + using process_ptr_t = void(*)(storage_ptr_t, storage_ptr_t); + using destructor_ptr_t = void(*)(storage_ptr_t); + + const invoke_ptr_t invoke_ptr; + const process_ptr_t copy_ptr; + const process_ptr_t move_ptr; + const destructor_ptr_t destructor_ptr; + + explicit constexpr vtable() noexcept : + invoke_ptr{ [](storage_ptr_t, Args&&...) -> R + { throw std::bad_function_call(); } + }, + copy_ptr{ [](storage_ptr_t, storage_ptr_t) noexcept -> void {} }, + move_ptr{ [](storage_ptr_t, storage_ptr_t) noexcept -> void {} }, + destructor_ptr{ [](storage_ptr_t) noexcept -> void {} } + {} + + template<typename C> explicit constexpr vtable(wrapper<C>) noexcept : + invoke_ptr{ [](storage_ptr_t storage_ptr, Args&&... args) + noexcept(noexcept(std::declval<C>()(args...))) -> R + { return (*static_cast<C*>(storage_ptr))( + std::forward<Args>(args)... + ); } + }, + copy_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) + noexcept(std::is_nothrow_copy_constructible<C>::value) -> void + { new (dst_ptr) C{ (*static_cast<C*>(src_ptr)) }; } + }, + move_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) + noexcept(std::is_nothrow_move_constructible<C>::value) -> void + { new (dst_ptr) C{ std::move(*static_cast<C*>(src_ptr)) }; } + }, + destructor_ptr{ [](storage_ptr_t storage_ptr) + noexcept -> void + { static_cast<C*>(storage_ptr)->~C(); } + } + {} + + vtable(const vtable&) = delete; + vtable(vtable&&) = delete; + + vtable& operator= (const vtable&) = delete; + vtable& operator= (vtable&&) = delete; + + ~vtable() = default; +}; + +template<size_t DstCap, size_t DstAlign, size_t SrcCap, size_t SrcAlign> +struct is_valid_inplace_dst : std::true_type +{ + static_assert(DstCap >= SrcCap, + "Can't squeeze larger inplace_function into a smaller one" + ); + + static_assert(DstAlign % SrcAlign == 0, + "Incompatible inplace_function alignments" + ); +}; + +} // namespace inplace_function_detail + +template< + typename Signature, + size_t Capacity = inplace_function_detail::InplaceFunctionDefaultCapacity, + size_t Alignment = std::alignment_of<typename inplace_function_detail::aligned_storage<Capacity>::type>::value +> +class inplace_function; // unspecified + +template< + typename R, + typename... Args, + size_t Capacity, + size_t Alignment +> +class inplace_function<R(Args...), Capacity, Alignment> +{ + static const constexpr inplace_function_detail::vtable<R, Args...> empty_vtable{}; +public: + using capacity = std::integral_constant<size_t, Capacity>; + using alignment = std::integral_constant<size_t, Alignment>; + + using storage_t = typename inplace_function_detail::aligned_storage<Capacity, Alignment>::type; + using vtable_t = inplace_function_detail::vtable<R, Args...>; + using vtable_ptr_t = const vtable_t*; + + template <typename, size_t, size_t> friend class inplace_function; + + inplace_function() noexcept : + vtable_ptr_{std::addressof(empty_vtable)} + {} + + template< + typename T, + typename C = typename std::decay<T>::type, + typename = typename std::enable_if< + !(std::is_same<C, inplace_function>::value + || std::is_convertible<C, inplace_function>::value) + >::type + > + inplace_function(T&& closure) + { +#if __cplusplus >= 201703L + static_assert(std::is_invocable_r<R, C, Args...>::value, + "inplace_function cannot be constructed from non-callable type" + ); +#endif + static_assert(std::is_copy_constructible<C>::value, + "inplace_function cannot be constructed from non-copyable type" + ); + + static_assert(sizeof(C) <= Capacity, + "inplace_function cannot be constructed from object with this (large) size" + ); + + static_assert(Alignment % std::alignment_of<C>::value == 0, + "inplace_function cannot be constructed from object with this (large) alignment" + ); + + static const vtable_t vt{inplace_function_detail::wrapper<C>{}}; + vtable_ptr_ = std::addressof(vt); + + new (std::addressof(storage_)) C{std::forward<T>(closure)}; + } + + inplace_function(std::nullptr_t) noexcept : + vtable_ptr_{std::addressof(empty_vtable)} + {} + + inplace_function(const inplace_function& other) : + vtable_ptr_{other.vtable_ptr_} + { + vtable_ptr_->copy_ptr( + std::addressof(storage_), + std::addressof(other.storage_) + ); + } + + inplace_function(inplace_function&& other) : + vtable_ptr_{other.vtable_ptr_} + { + vtable_ptr_->move_ptr( + std::addressof(storage_), + std::addressof(other.storage_) + ); + } + + inplace_function& operator= (std::nullptr_t) noexcept + { + vtable_ptr_->destructor_ptr(std::addressof(storage_)); + vtable_ptr_ = std::addressof(empty_vtable); + return *this; + } + + inplace_function& operator= (const inplace_function& other) + { + if(this != std::addressof(other)) + { + vtable_ptr_->destructor_ptr(std::addressof(storage_)); + + vtable_ptr_ = other.vtable_ptr_; + vtable_ptr_->copy_ptr( + std::addressof(storage_), + std::addressof(other.storage_) + ); + } + return *this; + } + + inplace_function& operator= (inplace_function&& other) + { + if(this != std::addressof(other)) + { + vtable_ptr_->destructor_ptr(std::addressof(storage_)); + + vtable_ptr_ = other.vtable_ptr_; + vtable_ptr_->move_ptr( + std::addressof(storage_), + std::addressof(other.storage_) + ); + } + return *this; + } + + ~inplace_function() + { + vtable_ptr_->destructor_ptr(std::addressof(storage_)); + } + + R operator() (Args... args) const + { + return vtable_ptr_->invoke_ptr( + std::addressof(storage_), + std::forward<Args>(args)... + ); + } + + constexpr bool operator== (std::nullptr_t) const noexcept + { + return !operator bool(); + } + + constexpr bool operator!= (std::nullptr_t) const noexcept + { + return operator bool(); + } + + explicit constexpr operator bool() const noexcept + { + return vtable_ptr_ != std::addressof(empty_vtable); + } + + template<size_t Cap, size_t Align> + operator inplace_function<R(Args...), Cap, Align>() const& + { + static_assert(inplace_function_detail::is_valid_inplace_dst< + Cap, Align, Capacity, Alignment + >::value, "conversion not allowed"); + + return {vtable_ptr_, vtable_ptr_->copy_ptr, std::addressof(storage_)}; + } + + template<size_t Cap, size_t Align> + operator inplace_function<R(Args...), Cap, Align>() && + { + static_assert(inplace_function_detail::is_valid_inplace_dst< + Cap, Align, Capacity, Alignment + >::value, "conversion not allowed"); + + return {vtable_ptr_, vtable_ptr_->move_ptr, std::addressof(storage_)}; + } + + void swap(inplace_function& other) + { + if (this == std::addressof(other)) return; + + storage_t tmp; + vtable_ptr_->move_ptr( + std::addressof(tmp), + std::addressof(storage_) + ); + vtable_ptr_->destructor_ptr(std::addressof(storage_)); + + other.vtable_ptr_->move_ptr( + std::addressof(storage_), + std::addressof(other.storage_) + ); + other.vtable_ptr_->destructor_ptr(std::addressof(other.storage_)); + + vtable_ptr_->move_ptr( + std::addressof(other.storage_), + std::addressof(tmp) + ); + vtable_ptr_->destructor_ptr(std::addressof(tmp)); + + std::swap(vtable_ptr_, other.vtable_ptr_); + } + +private: + vtable_ptr_t vtable_ptr_; + mutable storage_t storage_; + + inplace_function( + vtable_ptr_t vtable_ptr, + typename vtable_t::process_ptr_t process_ptr, + typename vtable_t::storage_ptr_t storage_ptr + ) : vtable_ptr_{vtable_ptr} + { + process_ptr(std::addressof(storage_), storage_ptr); + } +}; + +} // namespace stdext + +#endif /* _C4_EXT_SG14_INPLACE_FUNCTION_H_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/format.cpp b/thirdparty/ryml/ext/c4core/src/c4/format.cpp new file mode 100644 index 000000000..cc058c311 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/format.cpp @@ -0,0 +1,56 @@ +#include "c4/format.hpp" + +#include <memory> // for std::align + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wformat-nonliteral" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + +namespace c4 { + + +size_t to_chars(substr buf, fmt::const_raw_wrapper r) +{ + void * vptr = buf.str; + size_t space = buf.len; + auto ptr = (decltype(buf.str)) std::align(r.alignment, r.len, vptr, space); + if(ptr == nullptr) + { + // if it was not possible to align, return a conservative estimate + // of the required space + return r.alignment + r.len; + } + C4_CHECK(ptr >= buf.begin() && ptr <= buf.end()); + size_t sz = static_cast<size_t>(ptr - buf.str) + r.len; + if(sz <= buf.len) + { + memcpy(ptr, r.buf, r.len); + } + return sz; +} + + +bool from_chars(csubstr buf, fmt::raw_wrapper *r) +{ + void * vptr = (void*)buf.str; + size_t space = buf.len; + auto ptr = (decltype(buf.str)) std::align(r->alignment, r->len, vptr, space); + C4_CHECK(ptr != nullptr); + C4_CHECK(ptr >= buf.begin() && ptr <= buf.end()); + //size_t dim = (ptr - buf.str) + r->len; + memcpy(r->buf, ptr, r->len); + return true; +} + + +} // namespace c4 + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/thirdparty/ryml/ext/c4core/src/c4/format.hpp b/thirdparty/ryml/ext/c4core/src/c4/format.hpp new file mode 100644 index 000000000..589bd9842 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/format.hpp @@ -0,0 +1,900 @@ +#ifndef _C4_FORMAT_HPP_ +#define _C4_FORMAT_HPP_ + +/** @file format.hpp provides type-safe facilities for formatting arguments + * to string buffers */ + +#include "c4/charconv.hpp" +#include "c4/blob.hpp" + + +#ifdef _MSC_VER +# pragma warning(push) +# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017 +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' (performance warning) +# endif +# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe +#elif defined(__clang__) +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif + +namespace c4 { + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// formatting truthy types as booleans + +namespace fmt { + +/** write a variable as an alphabetic boolean, ie as either true or false + * @param strict_read */ +template<class T> +struct boolalpha_ +{ + boolalpha_(T val_, bool strict_read_=false) : val(val_ ? true : false), strict_read(strict_read_) {} + bool val; + bool strict_read; +}; + +template<class T> +boolalpha_<T> boolalpha(T const& val, bool strict_read=false) +{ + return boolalpha_<T>(val, strict_read); +} + +} // namespace fmt + +/** write a variable as an alphabetic boolean, ie as either true or false */ +template<class T> +inline size_t to_chars(substr buf, fmt::boolalpha_<T> fmt) +{ + return to_chars(buf, fmt.val ? "true" : "false"); +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// formatting integral types + +namespace fmt { + +/** format an integral type with a custom radix */ +template<typename T> +struct integral_ +{ + T val; + T radix; + C4_ALWAYS_INLINE integral_(T val_, T radix_) : val(val_), radix(radix_) {} +}; + +/** format an integral type with a custom radix, and pad with zeroes on the left */ +template<typename T> +struct integral_padded_ +{ + T val; + T radix; + size_t num_digits; + C4_ALWAYS_INLINE integral_padded_(T val_, T radix_, size_t nd) : val(val_), radix(radix_), num_digits(nd) {} +}; + +/** format an integral type with a custom radix */ +template<class T> +C4_ALWAYS_INLINE integral_<T> integral(T val, T radix=10) +{ + return integral_<T>(val, radix); +} +/** format an integral type with a custom radix */ +template<class T> +C4_ALWAYS_INLINE integral_<intptr_t> integral(T const* val, T radix=10) +{ + return integral_<intptr_t>(reinterpret_cast<intptr_t>(val), static_cast<intptr_t>(radix)); +} +/** format an integral type with a custom radix */ +template<class T> +C4_ALWAYS_INLINE integral_<intptr_t> integral(std::nullptr_t, T radix=10) +{ + return integral_<intptr_t>(intptr_t(0), static_cast<intptr_t>(radix)); +} +/** pad the argument with zeroes on the left, with decimal radix */ +template<class T> +C4_ALWAYS_INLINE integral_padded_<T> zpad(T val, size_t num_digits) +{ + return integral_padded_<T>(val, T(10), num_digits); +} +/** pad the argument with zeroes on the left */ +template<class T> +C4_ALWAYS_INLINE integral_padded_<T> zpad(integral_<T> val, size_t num_digits) +{ + return integral_padded_<T>(val.val, val.radix, num_digits); +} +/** pad the argument with zeroes on the left */ +C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(std::nullptr_t, size_t num_digits) +{ + return integral_padded_<intptr_t>(0, 16, num_digits); +} +/** pad the argument with zeroes on the left */ +template<class T> +C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T const* val, size_t num_digits) +{ + return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits); +} +template<class T> +C4_ALWAYS_INLINE integral_padded_<intptr_t> zpad(T * val, size_t num_digits) +{ + return integral_padded_<intptr_t>(reinterpret_cast<intptr_t>(val), 16, num_digits); +} + + +/** format the pointer as an hexadecimal value */ +template<class T> +inline integral_<intptr_t> hex(T * v) +{ + return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16)); +} +/** format the pointer as an hexadecimal value */ +template<class T> +inline integral_<intptr_t> hex(T const* v) +{ + return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(16)); +} +/** format null as an hexadecimal value + * @overload hex */ +inline integral_<intptr_t> hex(std::nullptr_t) +{ + return integral_<intptr_t>(0, intptr_t(16)); +} +/** format the integral_ argument as an hexadecimal value + * @overload hex */ +template<class T> +inline integral_<T> hex(T v) +{ + return integral_<T>(v, T(16)); +} + +/** format the pointer as an octal value */ +template<class T> +inline integral_<intptr_t> oct(T const* v) +{ + return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8)); +} +/** format the pointer as an octal value */ +template<class T> +inline integral_<intptr_t> oct(T * v) +{ + return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(8)); +} +/** format null as an octal value */ +inline integral_<intptr_t> oct(std::nullptr_t) +{ + return integral_<intptr_t>(intptr_t(0), intptr_t(8)); +} +/** format the integral_ argument as an octal value */ +template<class T> +inline integral_<T> oct(T v) +{ + return integral_<T>(v, T(8)); +} + +/** format the pointer as a binary 0-1 value + * @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */ +template<class T> +inline integral_<intptr_t> bin(T const* v) +{ + return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2)); +} +/** format the pointer as a binary 0-1 value + * @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */ +template<class T> +inline integral_<intptr_t> bin(T * v) +{ + return integral_<intptr_t>(reinterpret_cast<intptr_t>(v), intptr_t(2)); +} +/** format null as a binary 0-1 value + * @see c4::raw() if you want to use a binary memcpy instead of 0-1 formatting */ +inline integral_<intptr_t> bin(std::nullptr_t) +{ + return integral_<intptr_t>(intptr_t(0), intptr_t(2)); +} +/** format the integral_ argument as a binary 0-1 value + * @see c4::raw() if you want to use a raw memcpy-based binary dump instead of 0-1 formatting */ +template<class T> +inline integral_<T> bin(T v) +{ + return integral_<T>(v, T(2)); +} + + +template<class T> +struct overflow_checked_ +{ + static_assert(std::is_integral<T>::value, "range checking only for integral types"); + C4_ALWAYS_INLINE overflow_checked_(T &val_) : val(&val_) {} + T *val; +}; +template<class T> +C4_ALWAYS_INLINE overflow_checked_<T> overflow_checked(T &val) +{ + return overflow_checked_<T>(val); +} + +} // namespace fmt + +/** format an integral_ signed type */ +template<typename T> +C4_ALWAYS_INLINE +typename std::enable_if<std::is_signed<T>::value, size_t>::type +to_chars(substr buf, fmt::integral_<T> fmt) +{ + return itoa(buf, fmt.val, fmt.radix); +} +/** format an integral_ signed type, pad with zeroes */ +template<typename T> +C4_ALWAYS_INLINE +typename std::enable_if<std::is_signed<T>::value, size_t>::type +to_chars(substr buf, fmt::integral_padded_<T> fmt) +{ + return itoa(buf, fmt.val, fmt.radix, fmt.num_digits); +} + +/** format an integral_ unsigned type */ +template<typename T> +C4_ALWAYS_INLINE +typename std::enable_if<std::is_unsigned<T>::value, size_t>::type +to_chars(substr buf, fmt::integral_<T> fmt) +{ + return utoa(buf, fmt.val, fmt.radix); +} +/** format an integral_ unsigned type, pad with zeroes */ +template<typename T> +C4_ALWAYS_INLINE +typename std::enable_if<std::is_unsigned<T>::value, size_t>::type +to_chars(substr buf, fmt::integral_padded_<T> fmt) +{ + return utoa(buf, fmt.val, fmt.radix, fmt.num_digits); +} + +template<class T> +C4_ALWAYS_INLINE bool from_chars(csubstr s, fmt::overflow_checked_<T> wrapper) +{ + if(C4_LIKELY(!overflows<T>(s))) + return atox(s, wrapper.val); + return false; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// formatting real types + +namespace fmt { + +template<class T> +struct real_ +{ + T val; + int precision; + RealFormat_e fmt; + real_(T v, int prec=-1, RealFormat_e f=FTOA_FLOAT) : val(v), precision(prec), fmt(f) {} +}; + +template<class T> +real_<T> real(T val, int precision, RealFormat_e fmt=FTOA_FLOAT) +{ + return real_<T>(val, precision, fmt); +} + +} // namespace fmt + +inline size_t to_chars(substr buf, fmt::real_< float> fmt) { return ftoa(buf, fmt.val, fmt.precision, fmt.fmt); } +inline size_t to_chars(substr buf, fmt::real_<double> fmt) { return dtoa(buf, fmt.val, fmt.precision, fmt.fmt); } + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// writing raw binary data + +namespace fmt { + +/** @see blob_ */ +template<class T> +struct raw_wrapper_ : public blob_<T> +{ + size_t alignment; + + C4_ALWAYS_INLINE raw_wrapper_(blob_<T> data, size_t alignment_) noexcept + : + blob_<T>(data), + alignment(alignment_) + { + C4_ASSERT_MSG(alignment > 0 && (alignment & (alignment - 1)) == 0, "alignment must be a power of two"); + } +}; + +using const_raw_wrapper = raw_wrapper_<cbyte>; +using raw_wrapper = raw_wrapper_<byte>; + +/** mark a variable to be written in raw binary format, using memcpy + * @see blob_ */ +inline const_raw_wrapper craw(cblob data, size_t alignment=alignof(max_align_t)) +{ + return const_raw_wrapper(data, alignment); +} +/** mark a variable to be written in raw binary format, using memcpy + * @see blob_ */ +inline const_raw_wrapper raw(cblob data, size_t alignment=alignof(max_align_t)) +{ + return const_raw_wrapper(data, alignment); +} +/** mark a variable to be written in raw binary format, using memcpy + * @see blob_ */ +template<class T> +inline const_raw_wrapper craw(T const& C4_RESTRICT data, size_t alignment=alignof(T)) +{ + return const_raw_wrapper(cblob(data), alignment); +} +/** mark a variable to be written in raw binary format, using memcpy + * @see blob_ */ +template<class T> +inline const_raw_wrapper raw(T const& C4_RESTRICT data, size_t alignment=alignof(T)) +{ + return const_raw_wrapper(cblob(data), alignment); +} + +/** mark a variable to be read in raw binary format, using memcpy */ +inline raw_wrapper raw(blob data, size_t alignment=alignof(max_align_t)) +{ + return raw_wrapper(data, alignment); +} +/** mark a variable to be read in raw binary format, using memcpy */ +template<class T> +inline raw_wrapper raw(T & C4_RESTRICT data, size_t alignment=alignof(T)) +{ + return raw_wrapper(blob(data), alignment); +} + +} // namespace fmt + + +/** write a variable in raw binary format, using memcpy */ +C4CORE_EXPORT size_t to_chars(substr buf, fmt::const_raw_wrapper r); + +/** read a variable in raw binary format, using memcpy */ +C4CORE_EXPORT bool from_chars(csubstr buf, fmt::raw_wrapper *r); +/** read a variable in raw binary format, using memcpy */ +inline bool from_chars(csubstr buf, fmt::raw_wrapper r) +{ + return from_chars(buf, &r); +} + +/** read a variable in raw binary format, using memcpy */ +inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper *r) +{ + return from_chars(buf, r); +} +/** read a variable in raw binary format, using memcpy */ +inline size_t from_chars_first(csubstr buf, fmt::raw_wrapper r) +{ + return from_chars(buf, &r); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// formatting aligned to left/right + +namespace fmt { + +template<class T> +struct left_ +{ + T val; + size_t width; + char pad; + left_(T v, size_t w, char p) : val(v), width(w), pad(p) {} +}; + +template<class T> +struct right_ +{ + T val; + size_t width; + char pad; + right_(T v, size_t w, char p) : val(v), width(w), pad(p) {} +}; + +/** mark an argument to be aligned left */ +template<class T> +left_<T> left(T val, size_t width, char padchar=' ') +{ + return left_<T>(val, width, padchar); +} + +/** mark an argument to be aligned right */ +template<class T> +right_<T> right(T val, size_t width, char padchar=' ') +{ + return right_<T>(val, width, padchar); +} + +} // namespace fmt + + +template<class T> +size_t to_chars(substr buf, fmt::left_<T> const& C4_RESTRICT align) +{ + size_t ret = to_chars(buf, align.val); + if(ret >= buf.len || ret >= align.width) + return ret > align.width ? ret : align.width; + buf.first(align.width).sub(ret).fill(align.pad); + to_chars(buf, align.val); + return align.width; +} + +template<class T> +size_t to_chars(substr buf, fmt::right_<T> const& C4_RESTRICT align) +{ + size_t ret = to_chars(buf, align.val); + if(ret >= buf.len || ret >= align.width) + return ret > align.width ? ret : align.width; + size_t rem = static_cast<size_t>(align.width - ret); + buf.first(rem).fill(align.pad); + to_chars(buf.sub(rem), align.val); + return align.width; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/// @cond dev +// terminates the variadic recursion +inline size_t cat(substr /*buf*/) +{ + return 0; +} +/// @endcond + + +/** serialize the arguments, concatenating them to the given fixed-size buffer. + * The buffer size is strictly respected: no writes will occur beyond its end. + * @return the number of characters needed to write all the arguments into the buffer. + * @see c4::catrs() if instead of a fixed-size buffer, a resizeable container is desired + * @see c4::uncat() for the inverse function + * @see c4::catsep() if a separator between each argument is to be used + * @see c4::format() if a format string is desired */ +template<class Arg, class... Args> +size_t cat(substr buf, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + size_t num = to_chars(buf, a); + buf = buf.len >= num ? buf.sub(num) : substr{}; + num += cat(buf, more...); + return num; +} + +/** like c4::cat() but return a substr instead of a size */ +template<class... Args> +substr cat_sub(substr buf, Args && ...args) +{ + size_t sz = cat(buf, std::forward<Args>(args)...); + C4_CHECK(sz <= buf.len); + return {buf.str, sz <= buf.len ? sz : buf.len}; +} + + +//----------------------------------------------------------------------------- + +/// @cond dev +// terminates the variadic recursion +inline size_t uncat(csubstr /*buf*/) +{ + return 0; +} +/// @endcond + + +/** deserialize the arguments from the given buffer. + * + * @return the number of characters read from the buffer, or csubstr::npos + * if a conversion was not successful. + * @see c4::cat(). c4::uncat() is the inverse of c4::cat(). */ +template<class Arg, class... Args> +size_t uncat(csubstr buf, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more) +{ + size_t out = from_chars_first(buf, &a); + if(C4_UNLIKELY(out == csubstr::npos)) + return csubstr::npos; + buf = buf.len >= out ? buf.sub(out) : substr{}; + size_t num = uncat(buf, more...); + if(C4_UNLIKELY(num == csubstr::npos)) + return csubstr::npos; + return out + num; +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace detail { + +template<class Sep> +inline size_t catsep_more(substr /*buf*/, Sep const& C4_RESTRICT /*sep*/) +{ + return 0; +} + +template<class Sep, class Arg, class... Args> +size_t catsep_more(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + size_t ret = to_chars(buf, sep), num = ret; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = to_chars(buf, a); + num += ret; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = catsep_more(buf, sep, more...); + num += ret; + return num; +} + +template<class Sep> +inline size_t uncatsep_more(csubstr /*buf*/, Sep & /*sep*/) +{ + return 0; +} + +template<class Sep, class Arg, class... Args> +size_t uncatsep_more(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more) +{ + size_t ret = from_chars_first(buf, &sep), num = ret; + if(C4_UNLIKELY(ret == csubstr::npos)) + return csubstr::npos; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = from_chars_first(buf, &a); + if(C4_UNLIKELY(ret == csubstr::npos)) + return csubstr::npos; + num += ret; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = uncatsep_more(buf, sep, more...); + if(C4_UNLIKELY(ret == csubstr::npos)) + return csubstr::npos; + num += ret; + return num; +} + +} // namespace detail + + +/** serialize the arguments, concatenating them to the given fixed-size + * buffer, using a separator between each argument. + * The buffer size is strictly respected: no writes will occur beyond its end. + * @return the number of characters needed to write all the arguments into the buffer. + * @see c4::catseprs() if instead of a fixed-size buffer, a resizeable container is desired + * @see c4::uncatsep() for the inverse function (ie, reading instead of writing) + * @see c4::cat() if no separator is needed + * @see c4::format() if a format string is desired */ +template<class Sep, class Arg, class... Args> +size_t catsep(substr buf, Sep const& C4_RESTRICT sep, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + size_t num = to_chars(buf, a); + buf = buf.len >= num ? buf.sub(num) : substr{}; + num += detail::catsep_more(buf, sep, more...); + return num; +} + +/** like c4::catsep() but return a substr instead of a size + * @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */ +template<class... Args> +substr catsep_sub(substr buf, Args && ...args) +{ + size_t sz = catsep(buf, std::forward<Args>(args)...); + C4_CHECK(sz <= buf.len); + return {buf.str, sz <= buf.len ? sz : buf.len}; +} + +/** deserialize the arguments from the given buffer, using a separator. + * + * @return the number of characters read from the buffer, or csubstr::npos + * if a conversion was not successful + * @see c4::catsep(). c4::uncatsep() is the inverse of c4::catsep(). */ +template<class Sep, class Arg, class... Args> +size_t uncatsep(csubstr buf, Sep & C4_RESTRICT sep, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more) +{ + size_t ret = from_chars_first(buf, &a), num = ret; + if(C4_UNLIKELY(ret == csubstr::npos)) + return csubstr::npos; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = detail::uncatsep_more(buf, sep, more...); + if(C4_UNLIKELY(ret == csubstr::npos)) + return csubstr::npos; + num += ret; + return num; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/// @cond dev +// terminates the variadic recursion +inline size_t format(substr buf, csubstr fmt) +{ + return to_chars(buf, fmt); +} +/// @endcond + + +/** using a format string, serialize the arguments into the given + * fixed-size buffer. + * The buffer size is strictly respected: no writes will occur beyond its end. + * In the format string, each argument is marked with a compact + * curly-bracket pair: {}. Arguments beyond the last curly bracket pair + * are silently ignored. For example: + * @code{.cpp} + * c4::format(buf, "the {} drank {} {}", "partier", 5, "beers"); // the partier drank 5 beers + * c4::format(buf, "the {} drank {} {}", "programmer", 6, "coffees"); // the programmer drank 6 coffees + * @endcode + * @return the number of characters needed to write into the buffer. + * @see c4::formatrs() if instead of a fixed-size buffer, a resizeable container is desired + * @see c4::unformat() for the inverse function + * @see c4::cat() if no format or separator is needed + * @see c4::catsep() if no format is needed, but a separator must be used */ +template<class Arg, class... Args> +size_t format(substr buf, csubstr fmt, Arg const& C4_RESTRICT a, Args const& C4_RESTRICT ...more) +{ + size_t pos = fmt.find("{}"); // @todo use _find_fmt() + if(C4_UNLIKELY(pos == csubstr::npos)) + return to_chars(buf, fmt); + size_t num = to_chars(buf, fmt.sub(0, pos)); + size_t out = num; + buf = buf.len >= num ? buf.sub(num) : substr{}; + num = to_chars(buf, a); + out += num; + buf = buf.len >= num ? buf.sub(num) : substr{}; + num = format(buf, fmt.sub(pos + 2), more...); + out += num; + return out; +} + +/** like c4::format() but return a substr instead of a size + * @see c4::format() + * @see c4::catsep(). uncatsep() is the inverse of catsep(). */ +template<class... Args> +substr format_sub(substr buf, csubstr fmt, Args const& C4_RESTRICT ...args) +{ + size_t sz = c4::format(buf, fmt, args...); + C4_CHECK(sz <= buf.len); + return {buf.str, sz <= buf.len ? sz : buf.len}; +} + + +//----------------------------------------------------------------------------- + +/// @cond dev +// terminates the variadic recursion +inline size_t unformat(csubstr /*buf*/, csubstr fmt) +{ + return fmt.len; +} +/// @endcond + + +/** using a format string, deserialize the arguments from the given + * buffer. + * @return the number of characters read from the buffer, or npos if a conversion failed. + * @see c4::format(). c4::unformat() is the inverse function to format(). */ +template<class Arg, class... Args> +size_t unformat(csubstr buf, csubstr fmt, Arg & C4_RESTRICT a, Args & C4_RESTRICT ...more) +{ + const size_t pos = fmt.find("{}"); + if(C4_UNLIKELY(pos == csubstr::npos)) + return unformat(buf, fmt); + size_t num = pos; + size_t out = num; + buf = buf.len >= num ? buf.sub(num) : substr{}; + num = from_chars_first(buf, &a); + if(C4_UNLIKELY(num == csubstr::npos)) + return csubstr::npos; + out += num; + buf = buf.len >= num ? buf.sub(num) : substr{}; + num = unformat(buf, fmt.sub(pos + 2), more...); + if(C4_UNLIKELY(num == csubstr::npos)) + return csubstr::npos; + out += num; + return out; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** a tag type for marking append to container + * @see c4::catrs() */ +struct append_t {}; + +/** a tag variable + * @see c4::catrs() */ +constexpr const append_t append = {}; + + +//----------------------------------------------------------------------------- + +/** like c4::cat(), but receives a container, and resizes it as needed to contain + * the result. The container is overwritten. To append to it, use the append + * overload. + * @see c4::cat() */ +template<class CharOwningContainer, class... Args> +inline void catrs(CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args) +{ +retry: + substr buf = to_substr(*cont); + size_t ret = cat(buf, args...); + cont->resize(ret); + if(ret > buf.len) + goto retry; +} + +/** like c4::cat(), but creates and returns a new container sized as needed to contain + * the result. + * @see c4::cat() */ +template<class CharOwningContainer, class... Args> +inline CharOwningContainer catrs(Args const& C4_RESTRICT ...args) +{ + CharOwningContainer cont; + catrs(&cont, args...); + return cont; +} + +/** like c4::cat(), but receives a container, and appends to it instead of + * overwriting it. The container is resized as needed to contain the result. + * @return the region newly appended to the original container + * @see c4::cat() + * @see c4::catrs() */ +template<class CharOwningContainer, class... Args> +inline csubstr catrs(append_t, CharOwningContainer * C4_RESTRICT cont, Args const& C4_RESTRICT ...args) +{ + const size_t pos = cont->size(); +retry: + substr buf = to_substr(*cont).sub(pos); + size_t ret = cat(buf, args...); + cont->resize(pos + ret); + if(ret > buf.len) + goto retry; + return to_csubstr(*cont).range(pos, cont->size()); +} + + +//----------------------------------------------------------------------------- + +/// @cond dev +// terminates the recursion +template<class CharOwningContainer, class Sep, class... Args> +inline void catseprs(CharOwningContainer * C4_RESTRICT, Sep const& C4_RESTRICT) +{ + return; +} +/// @end cond + + +/** like c4::catsep(), but receives a container, and resizes it as needed to contain the result. + * The container is overwritten. To append to the container use the append overload. + * @see c4::catsep() */ +template<class CharOwningContainer, class Sep, class... Args> +inline void catseprs(CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args) +{ +retry: + substr buf = to_substr(*cont); + size_t ret = catsep(buf, sep, args...); + cont->resize(ret); + if(ret > buf.len) + goto retry; +} + +/** like c4::catsep(), but create a new container with the result. + * @return the requested container */ +template<class CharOwningContainer, class Sep, class... Args> +inline CharOwningContainer catseprs(Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args) +{ + CharOwningContainer cont; + catseprs(&cont, sep, args...); + return cont; +} + + +/// @cond dev +// terminates the recursion +template<class CharOwningContainer, class Sep, class... Args> +inline csubstr catseprs(append_t, CharOwningContainer * C4_RESTRICT, Sep const& C4_RESTRICT) +{ + csubstr s; + return s; +} +/// @endcond + +/** like catsep(), but receives a container, and appends the arguments, resizing the + * container as needed to contain the result. The buffer is appended to. + * @return a csubstr of the appended part + * @ingroup formatting_functions */ +template<class CharOwningContainer, class Sep, class... Args> +inline csubstr catseprs(append_t, CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args) +{ + const size_t pos = cont->size(); +retry: + substr buf = to_substr(*cont).sub(pos); + size_t ret = catsep(buf, sep, args...); + cont->resize(pos + ret); + if(ret > buf.len) + goto retry; + return to_csubstr(*cont).range(pos, cont->size()); +} + + +//----------------------------------------------------------------------------- + +/** like c4::format(), but receives a container, and resizes it as needed + * to contain the result. The container is overwritten. To append to + * the container use the append overload. + * @see c4::format() */ +template<class CharOwningContainer, class... Args> +inline void formatrs(CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args) +{ +retry: + substr buf = to_substr(*cont); + size_t ret = format(buf, fmt, args...); + cont->resize(ret); + if(ret > buf.len) + goto retry; +} + +/** like c4::format(), but create a new container with the result. + * @return the requested container */ +template<class CharOwningContainer, class... Args> +inline CharOwningContainer formatrs(csubstr fmt, Args const& C4_RESTRICT ...args) +{ + CharOwningContainer cont; + formatrs(&cont, fmt, args...); + return cont; +} + +/** like format(), but receives a container, and appends the + * arguments, resizing the container as needed to contain the + * result. The buffer is appended to. + * @return the region newly appended to the original container + * @ingroup formatting_functions */ +template<class CharOwningContainer, class... Args> +inline csubstr formatrs(append_t, CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args) +{ + const size_t pos = cont->size(); +retry: + substr buf = to_substr(*cont).sub(pos); + size_t ret = format(buf, fmt, args...); + cont->resize(pos + ret); + if(ret > buf.len) + goto retry; + return to_csubstr(*cont).range(pos, cont->size()); +} + +} // namespace c4 + +#ifdef _MSC_VER +# pragma warning(pop) +#elif defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#endif /* _C4_FORMAT_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/hash.hpp b/thirdparty/ryml/ext/c4core/src/c4/hash.hpp new file mode 100644 index 000000000..635bc1544 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/hash.hpp @@ -0,0 +1,95 @@ +#ifndef _C4_HASH_HPP_ +#define _C4_HASH_HPP_ + +#include "c4/config.hpp" +#include <climits> + +/** @file hash.hpp */ + +/** @defgroup hash Hash utils + * @see http://aras-p.info/blog/2016/08/02/Hash-Functions-all-the-way-down/ */ + +namespace c4 { + +namespace detail { + +/** @internal + * @ingroup hash + * @see this was taken a great answer in stackoverflow: + * https://stackoverflow.com/a/34597785/5875572 + * @see http://aras-p.info/blog/2016/08/02/Hash-Functions-all-the-way-down/ */ +template<typename ResultT, ResultT OffsetBasis, ResultT Prime> +class basic_fnv1a final +{ + + static_assert(std::is_unsigned<ResultT>::value, "need unsigned integer"); + +public: + + using result_type = ResultT; + +private: + + result_type state_ {}; + +public: + + C4_CONSTEXPR14 basic_fnv1a() noexcept : state_ {OffsetBasis} {} + + C4_CONSTEXPR14 void update(const void *const data, const size_t size) noexcept + { + auto cdata = static_cast<const unsigned char *>(data); + auto acc = this->state_; + for(size_t i = 0; i < size; ++i) + { + const auto next = size_t(cdata[i]); + acc = (acc ^ next) * Prime; + } + this->state_ = acc; + } + + C4_CONSTEXPR14 result_type digest() const noexcept + { + return this->state_; + } + +}; + +using fnv1a_32 = basic_fnv1a<uint32_t, UINT32_C( 2166136261), UINT32_C( 16777619)>; +using fnv1a_64 = basic_fnv1a<uint64_t, UINT64_C(14695981039346656037), UINT64_C(1099511628211)>; + +template<size_t Bits> struct fnv1a; +template<> struct fnv1a<32> { using type = fnv1a_32; }; +template<> struct fnv1a<64> { using type = fnv1a_64; }; + +} // namespace detail + + +/** @ingroup hash */ +template<size_t Bits> +using fnv1a_t = typename detail::fnv1a<Bits>::type; + + +/** @ingroup hash */ +C4_CONSTEXPR14 inline size_t hash_bytes(const void *const data, const size_t size) noexcept +{ + fnv1a_t<CHAR_BIT * sizeof(size_t)> fn{}; + fn.update(data, size); + return fn.digest(); +} + +/** + * @overload hash_bytes + * @ingroup hash */ +template<size_t N> +C4_CONSTEXPR14 inline size_t hash_bytes(const char (&str)[N]) noexcept +{ + fnv1a_t<CHAR_BIT * sizeof(size_t)> fn{}; + fn.update(str, N); + return fn.digest(); +} + +} // namespace c4 + + +#endif // _C4_HASH_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/language.cpp b/thirdparty/ryml/ext/c4core/src/c4/language.cpp new file mode 100644 index 000000000..d1b569741 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/language.cpp @@ -0,0 +1,16 @@ +#include "c4/language.hpp" + +namespace c4 { +namespace detail { + +#ifndef __GNUC__ +void use_char_pointer(char const volatile* v) +{ + C4_UNUSED(v); +} +#else +void foo() {} // to avoid empty file warning from the linker +#endif + +} // namespace detail +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/src/c4/language.hpp b/thirdparty/ryml/ext/c4core/src/c4/language.hpp new file mode 100644 index 000000000..90ff9bab5 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/language.hpp @@ -0,0 +1,275 @@ +#ifndef _C4_LANGUAGE_HPP_ +#define _C4_LANGUAGE_HPP_ + +/** @file language.hpp Provides language standard information macros and + * compiler agnostic utility macros: namespace facilities, function attributes, + * variable attributes, etc. + * @ingroup basic_headers */ + +#include "c4/preprocessor.hpp" +#include "c4/compiler.hpp" + +/* Detect C++ standard. + * @see http://stackoverflow.com/a/7132549/5875572 */ +#ifndef C4_CPP +# ifdef _MSC_VER +# if _MSC_VER >= 1910 // >VS2015: VS2017, VS2019 +# if (!defined(_MSVC_LANG)) +# error _MSVC not defined +# endif +# if _MSVC_LANG >= 201705L +# define C4_CPP 20 +# define C4_CPP20 +# elif _MSVC_LANG == 201703L +# define C4_CPP 17 +# define C4_CPP17 +# elif _MSVC_LANG >= 201402L +# define C4_CPP 14 +# define C4_CPP14 +# elif _MSVC_LANG >= 201103L +# define C4_CPP 11 +# define C4_CPP11 +# else +# error C++ lesser than C++11 not supported +# endif +# else +# if _MSC_VER == 1900 +# define C4_CPP 14 // VS2015 is c++14 https://devblogs.microsoft.com/cppblog/c111417-features-in-vs-2015-rtm/ +# define C4_CPP14 +# elif _MSC_VER == 1800 // VS2013 +# define C4_CPP 11 +# define C4_CPP11 +# else +# error C++ lesser than C++11 not supported +# endif +# endif +# elif defined(__INTEL_COMPILER) // https://software.intel.com/en-us/node/524490 +# ifdef __INTEL_CXX20_MODE__ // not sure about this +# define C4_CPP 20 +# define C4_CPP20 +# elif defined __INTEL_CXX17_MODE__ // not sure about this +# define C4_CPP 17 +# define C4_CPP17 +# elif defined __INTEL_CXX14_MODE__ // not sure about this +# define C4_CPP 14 +# define C4_CPP14 +# elif defined __INTEL_CXX11_MODE__ +# define C4_CPP 11 +# define C4_CPP11 +# else +# error C++ lesser than C++11 not supported +# endif +# else +# ifndef __cplusplus +# error __cplusplus is not defined? +# endif +# if __cplusplus == 1 +# error cannot handle __cplusplus==1 +# elif __cplusplus >= 201709L +# define C4_CPP 20 +# define C4_CPP20 +# elif __cplusplus >= 201703L +# define C4_CPP 17 +# define C4_CPP17 +# elif __cplusplus >= 201402L +# define C4_CPP 14 +# define C4_CPP14 +# elif __cplusplus >= 201103L +# define C4_CPP 11 +# define C4_CPP11 +# elif __cplusplus >= 199711L +# error C++ lesser than C++11 not supported +# endif +# endif +#else +# ifdef C4_CPP == 20 +# define C4_CPP20 +# elif C4_CPP == 17 +# define C4_CPP17 +# elif C4_CPP == 14 +# define C4_CPP14 +# elif C4_CPP == 11 +# define C4_CPP11 +# elif C4_CPP == 98 +# define C4_CPP98 +# error C++ lesser than C++11 not supported +# else +# error C4_CPP must be one of 20, 17, 14, 11, 98 +# endif +#endif + +#ifdef C4_CPP20 +# define C4_CPP17 +# define C4_CPP14 +# define C4_CPP11 +#elif defined(C4_CPP17) +# define C4_CPP14 +# define C4_CPP11 +#elif defined(C4_CPP14) +# define C4_CPP11 +#endif + +/** lifted from this answer: http://stackoverflow.com/a/20170989/5875572 */ +#ifndef _MSC_VER +# if __cplusplus < 201103 +# define C4_CONSTEXPR11 +# define C4_CONSTEXPR14 +//# define C4_NOEXCEPT +# elif __cplusplus == 201103 +# define C4_CONSTEXPR11 constexpr +# define C4_CONSTEXPR14 +//# define C4_NOEXCEPT noexcept +# else +# define C4_CONSTEXPR11 constexpr +# define C4_CONSTEXPR14 constexpr +//# define C4_NOEXCEPT noexcept +# endif +#else // _MSC_VER +# if _MSC_VER < 1900 +# define C4_CONSTEXPR11 +# define C4_CONSTEXPR14 +//# define C4_NOEXCEPT +# elif _MSC_VER < 2000 +# define C4_CONSTEXPR11 constexpr +# define C4_CONSTEXPR14 +//# define C4_NOEXCEPT noexcept +# else +# define C4_CONSTEXPR11 constexpr +# define C4_CONSTEXPR14 constexpr +//# define C4_NOEXCEPT noexcept +# endif +#endif // _MSC_VER + + +#if C4_CPP < 17 +#define C4_IF_CONSTEXPR +#define C4_INLINE_CONSTEXPR constexpr +#else +#define C4_IF_CONSTEXPR constexpr +#define C4_INLINE_CONSTEXPR inline constexpr +#endif + + +//------------------------------------------------------------ + +#define _C4_BEGIN_NAMESPACE(ns) namespace ns { +#define _C4_END_NAMESPACE(ns) } + +// MSVC cant handle the C4_FOR_EACH macro... need to fix this +//#define C4_BEGIN_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_BEGIN_NAMESPACE, , __VA_ARGS__) +//#define C4_END_NAMESPACE(...) C4_FOR_EACH_SEP(_C4_END_NAMESPACE, , __VA_ARGS__) +#define C4_BEGIN_NAMESPACE(ns) namespace ns { +#define C4_END_NAMESPACE(ns) } + +#define C4_BEGIN_HIDDEN_NAMESPACE namespace /*hidden*/ { +#define C4_END_HIDDEN_NAMESPACE } /* namespace hidden */ + +//------------------------------------------------------------ + +#ifndef C4_API +# if defined(_MSC_VER) +# if defined(C4_EXPORT) +# define C4_API __declspec(dllexport) +# elif defined(C4_IMPORT) +# define C4_API __declspec(dllimport) +# else +# define C4_API +# endif +# else +# define C4_API +# endif +#endif + +#ifndef _MSC_VER ///< @todo assuming gcc-like compiler. check it is actually so. +/** for function attributes in GCC, + * @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes */ +/** for __builtin functions in GCC, + * @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */ +# define C4_RESTRICT __restrict__ +# define C4_RESTRICT_FN __attribute__((restrict)) +# define C4_NO_INLINE __attribute__((noinline)) +# define C4_ALWAYS_INLINE inline __attribute__((always_inline)) +# define C4_CONST __attribute__((const)) +# define C4_PURE __attribute__((pure)) +/** force inlining of every callee function */ +# define C4_FLATTEN __atribute__((flatten)) +/** mark a function as hot, ie as having a visible impact in CPU time + * thus making it more likely to inline, etc + * @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */ +# define C4_HOT __attribute__((hot)) +/** mark a function as cold, ie as NOT having a visible impact in CPU time + * @see http://stackoverflow.com/questions/15028990/semantics-of-gcc-hot-attribute */ +# define C4_COLD __attribute__((cold)) +# define C4_EXPECT(x, y) __builtin_expect(x, y) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html +# define C4_LIKELY(x) __builtin_expect(x, 1) +# define C4_UNLIKELY(x) __builtin_expect(x, 0) +# define C4_UNREACHABLE() __builtin_unreachable() +# define C4_ATTR_FORMAT(...) //__attribute__((format (__VA_ARGS__))) ///< @see https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes +# define C4_NORETURN __attribute__((noreturn)) +#else +# define C4_RESTRICT __restrict +# define C4_RESTRICT_FN __declspec(restrict) +# define C4_NO_INLINE __declspec(noinline) +# define C4_ALWAYS_INLINE inline __forceinline +/** these are not available in VS AFAIK */ +# define C4_CONST +# define C4_PURE +# define C4_FLATTEN +# define C4_HOT /** @todo */ +# define C4_COLD /** @todo */ +# define C4_EXPECT(x, y) x /** @todo */ +# define C4_LIKELY(x) x /** @todo */ +# define C4_UNLIKELY(x) x /** @todo */ +# define C4_UNREACHABLE() /** @todo */ +# define C4_ATTR_FORMAT(...) /** */ +# define C4_NORETURN /** @todo */ +#endif + +#ifndef _MSC_VER +# define C4_FUNC __FUNCTION__ +# define C4_PRETTY_FUNC __PRETTY_FUNCTION__ +#else /// @todo assuming gcc-like compiler. check it is actually so. +# define C4_FUNC __FUNCTION__ +# define C4_PRETTY_FUNC __FUNCSIG__ +#endif + +/** prevent compiler warnings about a specific var being unused */ +#define C4_UNUSED(var) (void)var + +#if C4_CPP >= 17 +#define C4_STATIC_ASSERT(cond) static_assert(cond) +#else +#define C4_STATIC_ASSERT(cond) static_assert((cond), #cond) +#endif +#define C4_STATIC_ASSERT_MSG(cond, msg) static_assert((cond), #cond ": " msg) + +/** @def C4_DONT_OPTIMIZE idea lifted from GoogleBenchmark. + * @see https://github.com/google/benchmark/blob/master/include/benchmark/benchmark_api.h */ +namespace c4 { +namespace detail { +#ifdef __GNUC__ +# define C4_DONT_OPTIMIZE(var) c4::detail::dont_optimize(var) +template< class T > +C4_ALWAYS_INLINE void dont_optimize(T const& value) { asm volatile("" : : "g"(value) : "memory"); } +#else +# define C4_DONT_OPTIMIZE(var) c4::detail::use_char_pointer(reinterpret_cast< const char* >(&var)) +void use_char_pointer(char const volatile*); +#endif +} // namespace detail +} // namespace c4 + +/** @def C4_KEEP_EMPTY_LOOP prevent an empty loop from being optimized out. + * @see http://stackoverflow.com/a/7084193/5875572 */ +#ifndef _MSC_VER +# define C4_KEEP_EMPTY_LOOP { asm(""); } +#else +# define C4_KEEP_EMPTY_LOOP { char c; C4_DONT_OPTIMIZE(c); } +#endif + +/** @def C4_VA_LIST_REUSE_MUST_COPY + * @todo <jpmag> I strongly suspect that this is actually only in UNIX platforms. revisit this. */ +#ifdef __GNUC__ +# define C4_VA_LIST_REUSE_MUST_COPY +#endif + +#endif /* _C4_LANGUAGE_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/memory_resource.cpp b/thirdparty/ryml/ext/c4core/src/c4/memory_resource.cpp new file mode 100644 index 000000000..647d26736 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/memory_resource.cpp @@ -0,0 +1,338 @@ +#include "c4/memory_resource.hpp" +#include "c4/memory_util.hpp" + +#include <stdlib.h> +#include <string.h> +#if defined(C4_POSIX) || defined(C4_IOS) || defined(C4_MACOS) || defined(C4_ARM) +# include <errno.h> +#endif +#if defined(C4_ARM) +# include <malloc.h> +#endif + +#include <memory> + +namespace c4 { + +namespace detail { + + +#ifdef C4_NO_ALLOC_DEFAULTS +aalloc_pfn s_aalloc = nullptr; +free_pfn s_afree = nullptr; +arealloc_pfn s_arealloc = nullptr; +#else + + +void afree_impl(void *ptr) +{ +#if defined(C4_WIN) || defined(C4_XBOX) + ::_aligned_free(ptr); +#else + ::free(ptr); +#endif +} + + +void* aalloc_impl(size_t size, size_t alignment) +{ + void *mem; +#if defined(C4_WIN) || defined(C4_XBOX) + mem = ::_aligned_malloc(size, alignment); + C4_CHECK(mem != nullptr || size == 0); +#elif defined(C4_ARM) + // https://stackoverflow.com/questions/53614538/undefined-reference-to-posix-memalign-in-arm-gcc + // https://electronics.stackexchange.com/questions/467382/e2-studio-undefined-reference-to-posix-memalign/467753 + mem = memalign(alignment, size); + C4_CHECK(mem != nullptr || size == 0); +#elif defined(C4_POSIX) || defined(C4_IOS) || defined(C4_MACOS) + // NOTE: alignment needs to be sized in multiples of sizeof(void*) + size_t amult = alignment; + if(C4_UNLIKELY(alignment < sizeof(void*))) + { + amult = sizeof(void*); + } + int ret = ::posix_memalign(&mem, amult, size); + if(C4_UNLIKELY(ret)) + { + if(ret == EINVAL) + { + C4_ERROR("The alignment argument %zu was not a power of two, " + "or was not a multiple of sizeof(void*)", alignment); + } + else if(ret == ENOMEM) + { + C4_ERROR("There was insufficient memory to fulfill the " + "allocation request of %zu bytes (alignment=%lu)", size, size); + } + return nullptr; + } +#else + C4_NOT_IMPLEMENTED_MSG("need to implement an aligned allocation for this platform"); +#endif + C4_ASSERT_MSG((uintptr_t(mem) & (alignment-1)) == 0, "address %p is not aligned to %zu boundary", mem, alignment); + return mem; +} + + +void* arealloc_impl(void* ptr, size_t oldsz, size_t newsz, size_t alignment) +{ + /** @todo make this more efficient + * @see https://stackoverflow.com/questions/9078259/does-realloc-keep-the-memory-alignment-of-posix-memalign + * @see look for qReallocAligned() in http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qmalloc.cpp + */ + void *tmp = aalloc(newsz, alignment); + size_t min = newsz < oldsz ? newsz : oldsz; + if(mem_overlaps(ptr, tmp, oldsz, newsz)) + { + ::memmove(tmp, ptr, min); + } + else + { + ::memcpy(tmp, ptr, min); + } + afree(ptr); + return tmp; +} + +aalloc_pfn s_aalloc = aalloc_impl; +afree_pfn s_afree = afree_impl; +arealloc_pfn s_arealloc = arealloc_impl; + +#endif // C4_NO_ALLOC_DEFAULTS + +} // namespace detail + + +aalloc_pfn get_aalloc() +{ + return detail::s_aalloc; +} +void set_aalloc(aalloc_pfn fn) +{ + detail::s_aalloc = fn; +} + +afree_pfn get_afree() +{ + return detail::s_afree; +} +void set_afree(afree_pfn fn) +{ + detail::s_afree = fn; +} + +arealloc_pfn get_arealloc() +{ + return detail::s_arealloc; +} +void set_arealloc(arealloc_pfn fn) +{ + detail::s_arealloc = fn; +} + + +void* aalloc(size_t sz, size_t alignment) +{ + C4_ASSERT_MSG(c4::get_aalloc() != nullptr, "did you forget to call set_aalloc()?"); + auto fn = c4::get_aalloc(); + void* ptr = fn(sz, alignment); + return ptr; +} + +void afree(void* ptr) +{ + C4_ASSERT_MSG(c4::get_afree() != nullptr, "did you forget to call set_afree()?"); + auto fn = c4::get_afree(); + fn(ptr); +} + +void* arealloc(void *ptr, size_t oldsz, size_t newsz, size_t alignment) +{ + C4_ASSERT_MSG(c4::get_arealloc() != nullptr, "did you forget to call set_arealloc()?"); + auto fn = c4::get_arealloc(); + void* nptr = fn(ptr, oldsz, newsz, alignment); + return nptr; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void detail::_MemoryResourceSingleChunk::release() +{ + if(m_mem && m_owner) + { + impl_type::deallocate(m_mem, m_size); + } + m_mem = nullptr; + m_size = 0; + m_owner = false; + m_pos = 0; +} + +void detail::_MemoryResourceSingleChunk::acquire(size_t sz) +{ + clear(); + m_owner = true; + m_mem = (char*) impl_type::allocate(sz, alignof(max_align_t)); + m_size = sz; + m_pos = 0; +} + +void detail::_MemoryResourceSingleChunk::acquire(void *mem, size_t sz) +{ + clear(); + m_owner = false; + m_mem = (char*) mem; + m_size = sz; + m_pos = 0; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void* MemoryResourceLinear::do_allocate(size_t sz, size_t alignment, void *hint) +{ + C4_UNUSED(hint); + if(sz == 0) return nullptr; + // make sure there's enough room to allocate + if(m_pos + sz > m_size) + { + C4_ERROR("out of memory"); + return nullptr; + } + void *mem = m_mem + m_pos; + size_t space = m_size - m_pos; + if(std::align(alignment, sz, mem, space)) + { + C4_ASSERT(m_pos <= m_size); + C4_ASSERT(m_size - m_pos >= space); + m_pos += (m_size - m_pos) - space; + m_pos += sz; + C4_ASSERT(m_pos <= m_size); + } + else + { + C4_ERROR("could not align memory"); + mem = nullptr; + } + return mem; +} + +void MemoryResourceLinear::do_deallocate(void* ptr, size_t sz, size_t alignment) +{ + C4_UNUSED(ptr); + C4_UNUSED(sz); + C4_UNUSED(alignment); + // nothing to do!! +} + +void* MemoryResourceLinear::do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) +{ + if(newsz == oldsz) return ptr; + // is ptr the most recently allocated (MRA) block? + char *cptr = (char*)ptr; + bool same_pos = (m_mem + m_pos == cptr + oldsz); + // no need to get more memory when shrinking + if(newsz < oldsz) + { + // if this is the MRA, we can safely shrink the position + if(same_pos) + { + m_pos -= oldsz - newsz; + } + return ptr; + } + // we're growing the block, and it fits in size + else if(same_pos && cptr + newsz <= m_mem + m_size) + { + // if this is the MRA, we can safely shrink the position + m_pos += newsz - oldsz; + return ptr; + } + // we're growing the block or it doesn't fit - + // delegate any of these situations to do_deallocate() + return do_allocate(newsz, alignment, ptr); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** @todo add a free list allocator. A good candidate because of its + * small size is TLSF. + * + * @see https://github.com/mattconte/tlsf + * + * Comparisons: + * + * @see https://www.researchgate.net/publication/262375150_A_Comparative_Study_on_Memory_Allocators_in_Multicore_and_Multithreaded_Applications_-_SBESC_2011_-_Presentation_Slides + * @see http://webkit.sed.hu/blog/20100324/war-allocators-tlsf-action + * @see https://github.com/emeryberger/Malloc-Implementations/tree/master/allocators + * + * */ + +} // namespace c4 + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +#ifdef C4_REDEFINE_CPPNEW +#include <new> +void* operator new(size_t size) +{ + auto *mr = ::c4::get_memory_resource(); + return mr->allocate(size); +} +void operator delete(void *p) noexcept +{ + C4_NEVER_REACH(); +} +void operator delete(void *p, size_t size) +{ + auto *mr = ::c4::get_memory_resource(); + mr->deallocate(p, size); +} +void* operator new[](size_t size) +{ + return operator new(size); +} +void operator delete[](void *p) noexcept +{ + operator delete(p); +} +void operator delete[](void *p, size_t size) +{ + operator delete(p, size); +} +void* operator new(size_t size, std::nothrow_t) +{ + return operator new(size); +} +void operator delete(void *p, std::nothrow_t) +{ + operator delete(p); +} +void operator delete(void *p, size_t size, std::nothrow_t) +{ + operator delete(p, size); +} +void* operator new[](size_t size, std::nothrow_t) +{ + return operator new(size); +} +void operator delete[](void *p, std::nothrow_t) +{ + operator delete(p); +} +void operator delete[](void *p, size_t, std::nothrow_t) +{ + operator delete(p, size); +} +#endif // C4_REDEFINE_CPPNEW diff --git a/thirdparty/ryml/ext/c4core/src/c4/memory_resource.hpp b/thirdparty/ryml/ext/c4core/src/c4/memory_resource.hpp new file mode 100644 index 000000000..0818f5874 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/memory_resource.hpp @@ -0,0 +1,568 @@ +#ifndef _C4_MEMORY_RESOURCE_HPP_ +#define _C4_MEMORY_RESOURCE_HPP_ + +/** @file memory_resource.hpp Provides facilities to allocate typeless + * memory, via the memory resource model consecrated with C++17. */ + +/** @defgroup memory memory utilities */ + +/** @defgroup raw_memory_alloc Raw memory allocation + * @ingroup memory + */ + +/** @defgroup memory_resources Memory resources + * @ingroup memory + */ + +#include "c4/config.hpp" +#include "c4/error.hpp" + +namespace c4 { + +// need these forward decls here +struct MemoryResource; +struct MemoryResourceMalloc; +struct MemoryResourceStack; +MemoryResourceMalloc* get_memory_resource_malloc(); +MemoryResourceStack* get_memory_resource_stack(); +namespace detail { MemoryResource*& get_memory_resource(); } + + +// c-style allocation --------------------------------------------------------- + +// this API provides aligned allocation functions. +// These functions forward the call to a user-modifiable function. + + +// aligned allocation. + +/** Aligned allocation. Merely calls the current get_aalloc() function. + * @see get_aalloc() + * @ingroup raw_memory_alloc */ +void* aalloc(size_t sz, size_t alignment); + +/** Aligned free. Merely calls the current get_afree() function. + * @see get_afree() + * @ingroup raw_memory_alloc */ +void afree(void* ptr); + +/** Aligned reallocation. Merely calls the current get_arealloc() function. + * @see get_arealloc() + * @ingroup raw_memory_alloc */ +void* arealloc(void* ptr, size_t oldsz, size_t newsz, size_t alignment); + + +// allocation setup facilities. + +/** Function pointer type for aligned allocation + * @see set_aalloc() + * @ingroup raw_memory_alloc */ +using aalloc_pfn = void* (*)(size_t size, size_t alignment); + +/** Function pointer type for aligned deallocation + * @see set_afree() + * @ingroup raw_memory_alloc */ +using afree_pfn = void (*)(void *ptr); + +/** Function pointer type for aligned reallocation + * @see set_arealloc() + * @ingroup raw_memory_alloc */ +using arealloc_pfn = void* (*)(void *ptr, size_t oldsz, size_t newsz, size_t alignment); + + +// allocation function pointer setters/getters + +/** Set the global aligned allocation function. + * @see aalloc() + * @see get_aalloc() + * @ingroup raw_memory_alloc */ +void set_aalloc(aalloc_pfn fn); + +/** Set the global aligned deallocation function. + * @see afree() + * @see get_afree() + * @ingroup raw_memory_alloc */ +void set_afree(afree_pfn fn); + +/** Set the global aligned reallocation function. + * @see arealloc() + * @see get_arealloc() + * @ingroup raw_memory_alloc */ +void set_arealloc(arealloc_pfn fn); + + +/** Get the global aligned reallocation function. + * @see arealloc() + * @ingroup raw_memory_alloc */ +aalloc_pfn get_aalloc(); + +/** Get the global aligned deallocation function. + * @see afree() + * @ingroup raw_memory_alloc */ +afree_pfn get_afree(); + +/** Get the global aligned reallocation function. + * @see arealloc() + * @ingroup raw_memory_alloc */ +arealloc_pfn get_arealloc(); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// c++-style allocation ------------------------------------------------------- + +/** C++17-style memory_resource base class. See http://en.cppreference.com/w/cpp/experimental/memory_resource + * @ingroup memory_resources */ +struct MemoryResource +{ + const char *name = nullptr; + virtual ~MemoryResource() {} + + void* allocate(size_t sz, size_t alignment=alignof(max_align_t), void *hint=nullptr) + { + void *mem = this->do_allocate(sz, alignment, hint); + C4_CHECK_MSG(mem != nullptr, "could not allocate %lu bytes", sz); + return mem; + } + + void* reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment=alignof(max_align_t)) + { + void *mem = this->do_reallocate(ptr, oldsz, newsz, alignment); + C4_CHECK_MSG(mem != nullptr, "could not reallocate from %lu to %lu bytes", oldsz, newsz); + return mem; + } + + void deallocate(void* ptr, size_t sz, size_t alignment=alignof(max_align_t)) + { + this->do_deallocate(ptr, sz, alignment); + } + +protected: + + virtual void* do_allocate(size_t sz, size_t alignment, void* hint) = 0; + virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) = 0; + virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) = 0; + +}; + +/** get the current global memory resource. To avoid static initialization + * order problems, this is implemented using a function call to ensure + * that it is available when first used. + * @ingroup memory_resources */ +C4_ALWAYS_INLINE MemoryResource* get_memory_resource() +{ + return detail::get_memory_resource(); +} + +/** set the global memory resource + * @ingroup memory_resources */ +C4_ALWAYS_INLINE void set_memory_resource(MemoryResource* mr) +{ + C4_ASSERT(mr != nullptr); + detail::get_memory_resource() = mr; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** A c4::aalloc-based memory resource. Thread-safe if the implementation + * called by c4::aalloc() is safe. + * @ingroup memory_resources */ +struct MemoryResourceMalloc : public MemoryResource +{ + + MemoryResourceMalloc() { name = "malloc"; } + virtual ~MemoryResourceMalloc() override {} + +protected: + + virtual void* do_allocate(size_t sz, size_t alignment, void *hint) override + { + C4_UNUSED(hint); + return c4::aalloc(sz, alignment); + } + + virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override + { + C4_UNUSED(sz); + C4_UNUSED(alignment); + c4::afree(ptr); + } + + virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override + { + return c4::arealloc(ptr, oldsz, newsz, alignment); + } + +}; + +/** returns a malloc-based memory resource + * @ingroup memory_resources */ +C4_ALWAYS_INLINE MemoryResourceMalloc* get_memory_resource_malloc() +{ + /** @todo use a nifty counter: + * https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter */ + static MemoryResourceMalloc mr; + return &mr; +} + +namespace detail { +C4_ALWAYS_INLINE MemoryResource* & get_memory_resource() +{ + /** @todo use a nifty counter: + * https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter */ + thread_local static MemoryResource* mr = get_memory_resource_malloc(); + return mr; +} +} // namespace detail + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace detail { + +/** Allows a memory resource to obtain its memory from another memory resource. + * @ingroup memory_resources */ +struct DerivedMemoryResource : public MemoryResource +{ +public: + + DerivedMemoryResource(MemoryResource *mr_=nullptr) : m_local(mr_ ? mr_ : get_memory_resource()) {} + +private: + + MemoryResource *m_local; + +protected: + + virtual void* do_allocate(size_t sz, size_t alignment, void* hint) override + { + return m_local->allocate(sz, alignment, hint); + } + + virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override + { + return m_local->reallocate(ptr, oldsz, newsz, alignment); + } + + virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override + { + return m_local->deallocate(ptr, sz, alignment); + } +}; + +/** Provides common facilities for memory resource consisting of a single memory block + * @ingroup memory_resources */ +struct _MemoryResourceSingleChunk : public DerivedMemoryResource +{ + + C4_NO_COPY_OR_MOVE(_MemoryResourceSingleChunk); + + using impl_type = DerivedMemoryResource; + +public: + + _MemoryResourceSingleChunk(MemoryResource *impl=nullptr) : DerivedMemoryResource(impl) { name = "linear_malloc"; } + + /** initialize with owned memory, allocated from the given (or the global) memory resource */ + _MemoryResourceSingleChunk(size_t sz, MemoryResource *impl=nullptr) : _MemoryResourceSingleChunk(impl) { acquire(sz); } + /** initialize with borrowed memory */ + _MemoryResourceSingleChunk(void *mem, size_t sz) : _MemoryResourceSingleChunk() { acquire(mem, sz); } + + virtual ~_MemoryResourceSingleChunk() override { release(); } + +public: + + void const* mem() const { return m_mem; } + + size_t capacity() const { return m_size; } + size_t size() const { return m_pos; } + size_t slack() const { C4_ASSERT(m_size >= m_pos); return m_size - m_pos; } + +public: + + char *m_mem{nullptr}; + size_t m_size{0}; + size_t m_pos{0}; + bool m_owner; + +public: + + /** set the internal pointer to the beginning of the linear buffer */ + void clear() { m_pos = 0; } + + /** initialize with owned memory, allocated from the global memory resource */ + void acquire(size_t sz); + /** initialize with borrowed memory */ + void acquire(void *mem, size_t sz); + /** release the memory */ + void release(); + +}; + +} // namespace detail + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** provides a linear memory resource. Allocates incrementally from a linear + * buffer, without ever deallocating. Deallocations are a no-op, and the + * memory is freed only when the resource is release()d. The memory used by + * this object can be either owned or borrowed. When borrowed, no calls to + * malloc/free take place. + * + * @ingroup memory_resources */ +struct MemoryResourceLinear : public detail::_MemoryResourceSingleChunk +{ + + C4_NO_COPY_OR_MOVE(MemoryResourceLinear); + +public: + + using detail::_MemoryResourceSingleChunk::_MemoryResourceSingleChunk; + +protected: + + virtual void* do_allocate(size_t sz, size_t alignment, void *hint) override; + virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override; + virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override; +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** provides a stack-type malloc-based memory resource. + * @ingroup memory_resources */ +struct MemoryResourceStack : public detail::_MemoryResourceSingleChunk +{ + + C4_NO_COPY_OR_MOVE(MemoryResourceStack); + +public: + + using detail::_MemoryResourceSingleChunk::_MemoryResourceSingleChunk; + +protected: + + virtual void* do_allocate(size_t sz, size_t alignment, void *hint) override; + virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override; + virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override; +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** provides a linear array-based memory resource. + * @see MemoryResourceLinear + * @ingroup memory_resources */ +template<size_t N> +struct MemoryResourceLinearArr : public MemoryResourceLinear +{ + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable: 4324) // structure was padded due to alignment specifier + #endif + alignas(alignof(max_align_t)) char m_arr[N]; + #ifdef _MSC_VER + #pragma warning(pop) + #endif + MemoryResourceLinearArr() : MemoryResourceLinear(m_arr, N) { name = "linear_arr"; } +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +struct AllocationCounts +{ + struct Item + { + ssize_t allocs; + ssize_t size; + + void add(size_t sz) + { + ++allocs; + size += static_cast<ssize_t>(sz); + } + void rem(size_t sz) + { + --allocs; + size -= static_cast<ssize_t>(sz); + } + Item max(Item const& that) const + { + Item r(*this); + r.allocs = r.allocs > that.allocs ? r.allocs : that.allocs; + r.size = r.size > that.size ? r.size : that.size; + return r; + } + }; + + Item curr = {0, 0}; + Item total = {0, 0}; + Item max = {0, 0}; + + void clear_counts() + { + curr = {0, 0}; + total = {0, 0}; + max = {0, 0}; + } + + void update(AllocationCounts const& that) + { + curr.allocs += that.curr.allocs; + curr.size += that.curr.size; + total.allocs += that.total.allocs; + total.size += that.total.size; + max.allocs += that.max.allocs; + max.size += that.max.size; + } + + void add_counts(void* ptr, size_t sz) + { + if(ptr == nullptr) return; + curr.add(sz); + total.add(sz); + max = max.max(curr); + } + + void rem_counts(void *ptr, size_t sz) + { + if(ptr == nullptr) return; + curr.rem(sz); + } + + AllocationCounts operator- (AllocationCounts const& that) const + { + AllocationCounts r(*this); + r.curr.allocs -= that.curr.allocs; + r.curr.size -= that.curr.size; + r.total.allocs -= that.total.allocs; + r.total.size -= that.total.size; + r.max.allocs -= that.max.allocs; + r.max.size -= that.max.size; + return r; + } + + AllocationCounts operator+ (AllocationCounts const& that) const + { + AllocationCounts r(*this); + r.curr.allocs += that.curr.allocs; + r.curr.size += that.curr.size; + r.total.allocs += that.total.allocs; + r.total.size += that.total.size; + r.max.allocs += that.max.allocs; + r.max.size += that.max.size; + return r; + } +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** a MemoryResource which latches onto another MemoryResource + * and counts allocations and sizes. + * @ingroup memory_resources */ +class MemoryResourceCounts : public MemoryResource +{ +public: + + MemoryResourceCounts() : m_resource(get_memory_resource()) + { + C4_ASSERT(m_resource != this); + name = "MemoryResourceCounts"; + } + MemoryResourceCounts(MemoryResource *res) : m_resource(res) + { + C4_ASSERT(m_resource != this); + name = "MemoryResourceCounts"; + } + + MemoryResource *resource() { return m_resource; } + AllocationCounts const& counts() const { return m_counts; } + +protected: + + MemoryResource *m_resource; + AllocationCounts m_counts; + +protected: + + virtual void* do_allocate(size_t sz, size_t alignment, void * /*hint*/) override + { + void *ptr = m_resource->allocate(sz, alignment); + m_counts.add_counts(ptr, sz); + return ptr; + } + + virtual void do_deallocate(void* ptr, size_t sz, size_t alignment) override + { + m_counts.rem_counts(ptr, sz); + m_resource->deallocate(ptr, sz, alignment); + } + + virtual void* do_reallocate(void* ptr, size_t oldsz, size_t newsz, size_t alignment) override + { + m_counts.rem_counts(ptr, oldsz); + void* nptr = m_resource->reallocate(ptr, oldsz, newsz, alignment); + m_counts.add_counts(nptr, newsz); + return nptr; + } + +}; + +//----------------------------------------------------------------------------- +/** RAII class which binds a memory resource with a scope duration. + * @ingroup memory_resources */ +struct ScopedMemoryResource +{ + MemoryResource *m_original; + + ScopedMemoryResource(MemoryResource *r) + : + m_original(get_memory_resource()) + { + set_memory_resource(r); + } + + ~ScopedMemoryResource() + { + set_memory_resource(m_original); + } +}; + +//----------------------------------------------------------------------------- +/** RAII class which counts allocations and frees inside a scope. Can + * optionally set also the memory resource to be used. + * @ingroup memory_resources */ +struct ScopedMemoryResourceCounts +{ + MemoryResourceCounts mr; + + ScopedMemoryResourceCounts() : mr() + { + set_memory_resource(&mr); + } + ScopedMemoryResourceCounts(MemoryResource *m) : mr(m) + { + set_memory_resource(&mr); + } + ~ScopedMemoryResourceCounts() + { + set_memory_resource(mr.resource()); + } +}; + +} // namespace c4 + +#endif /* _C4_MEMORY_RESOURCE_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/memory_util.cpp b/thirdparty/ryml/ext/c4core/src/c4/memory_util.cpp new file mode 100644 index 000000000..981c62bb1 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/memory_util.cpp @@ -0,0 +1,30 @@ +#include "c4/memory_util.hpp" +#include "c4/error.hpp" + +namespace c4 { + +/** Fills 'dest' with the first 'pattern_size' bytes at 'pattern', 'num_times'. */ +void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times) +{ + if(C4_UNLIKELY(num_times == 0)) + return; + C4_ASSERT( ! mem_overlaps(dest, pattern, num_times*pattern_size, pattern_size)); + char *begin = (char*)dest; + char *end = begin + num_times * pattern_size; + // copy the pattern once + ::memcpy(begin, pattern, pattern_size); + // now copy from dest to itself, doubling up every time + size_t n = pattern_size; + while(begin + 2*n < end) + { + ::memcpy(begin + n, begin, n); + n <<= 1; // double n + } + // copy the missing part + if(begin + n < end) + { + ::memcpy(begin + n, begin, static_cast<size_t>(end - (begin + n))); + } +} + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/src/c4/memory_util.hpp b/thirdparty/ryml/ext/c4core/src/c4/memory_util.hpp new file mode 100644 index 000000000..7dfb263ba --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/memory_util.hpp @@ -0,0 +1,774 @@ +#ifndef _C4_MEMORY_UTIL_HPP_ +#define _C4_MEMORY_UTIL_HPP_ + +#include "c4/config.hpp" +#include "c4/error.hpp" +#include "c4/compiler.hpp" +#include "c4/cpu.hpp" +#ifdef C4_MSVC +#include <intrin.h> +#endif +#include <string.h> + +#if (defined(__GNUC__) && __GNUC__ >= 10) || defined(__has_builtin) +#define _C4_USE_LSB_INTRINSIC(which) __has_builtin(which) +#define _C4_USE_MSB_INTRINSIC(which) __has_builtin(which) +#elif defined(C4_MSVC) +#define _C4_USE_LSB_INTRINSIC(which) true +#define _C4_USE_MSB_INTRINSIC(which) true +#else +// let's try our luck +#define _C4_USE_LSB_INTRINSIC(which) true +#define _C4_USE_MSB_INTRINSIC(which) true +#endif + + +/** @file memory_util.hpp Some memory utilities. */ + +namespace c4 { + +/** set the given memory to zero */ +C4_ALWAYS_INLINE void mem_zero(void* mem, size_t num_bytes) +{ + memset(mem, 0, num_bytes); +} +/** set the given memory to zero */ +template<class T> +C4_ALWAYS_INLINE void mem_zero(T* mem, size_t num_elms) +{ + memset(mem, 0, sizeof(T) * num_elms); +} +/** set the given memory to zero */ +template<class T> +C4_ALWAYS_INLINE void mem_zero(T* mem) +{ + memset(mem, 0, sizeof(T)); +} + +C4_ALWAYS_INLINE C4_CONST bool mem_overlaps(void const* a, void const* b, size_t sza, size_t szb) +{ + // thanks @timwynants + return (((const char*)b + szb) > a && b < ((const char*)a+sza)); +} + +void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num_times); + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T> +C4_ALWAYS_INLINE C4_CONST bool is_aligned(T *ptr, uintptr_t alignment=alignof(T)) +{ + return (uintptr_t(ptr) & (alignment - uintptr_t(1))) == uintptr_t(0); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// least significant bit + +/** @name msb Compute the least significant bit + * @note the input value must be nonzero + * @note the input type must be unsigned + */ +/** @{ */ + +// https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear +#define _c4_lsb_fallback \ + unsigned c = 0; \ + v = (v ^ (v - 1)) >> 1; /* Set v's trailing 0s to 1s and zero rest */ \ + for(; v; ++c) \ + v >>= 1; \ + return (unsigned) c + +// u8 +template<class I> +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 1u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctz) + // upcast to use the intrinsic, it's cheaper. + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward(&bit, (unsigned long)v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctz((unsigned)v); + #endif + #else + _c4_lsb_fallback; + #endif +} + +// u16 +template<class I> +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 2u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctz) + // upcast to use the intrinsic, it's cheaper. + // Then remember that the upcast makes it to 31bits + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward(&bit, (unsigned long)v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctz((unsigned)v); + #endif + #else + _c4_lsb_fallback; + #endif +} + +// u32 +template<class I> +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 4u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctz) + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward(&bit, v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctz((unsigned)v); + #endif + #else + _c4_lsb_fallback; + #endif +} + +// u64 in 64bits +template<class I> +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctzl) + #if defined(C4_MSVC) + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward64(&bit, v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctzl((unsigned long)v); + #endif + #else + _c4_lsb_fallback; + #endif +} + +// u64 in 32bits +template<class I> +C4_CONSTEXPR14 +auto lsb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_LSB_INTRINSIC(__builtin_ctzll) + #if defined(C4_MSVC) + #if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanForward64(&bit, v); + return bit; + #else + _c4_lsb_fallback; + #endif + #else + return (unsigned)__builtin_ctzll((unsigned long long)v); + #endif + #else + _c4_lsb_fallback; + #endif +} + +#undef _c4_lsb_fallback + +/** @} */ + + +namespace detail { +template<class I, I val, unsigned num_bits, bool finished> struct _lsb11; +template<class I, I val, unsigned num_bits> +struct _lsb11<I, val, num_bits, false> +{ + enum : unsigned { num = _lsb11<I, (val>>1), num_bits+I(1), (((val>>1)&I(1))!=I(0))>::num }; +}; +template<class I, I val, unsigned num_bits> +struct _lsb11<I, val, num_bits, true> +{ + enum : unsigned { num = num_bits }; +}; +} // namespace detail + + +/** TMP version of lsb(); this needs to be implemented with template + * meta-programming because C++11 cannot use a constexpr function with + * local variables + * @see lsb */ +template<class I, I number> +struct lsb11 +{ + static_assert(number != 0, "lsb: number must be nonzero"); + enum : unsigned { value = detail::_lsb11<I, number, 0, ((number&I(1))!=I(0))>::num}; +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// most significant bit + + +/** @name msb Compute the most significant bit + * @note the input value must be nonzero + * @note the input type must be unsigned + */ +/** @{ */ + + +#define _c4_msb8_fallback \ + unsigned n = 0; \ + if(v & I(0xf0)) v >>= 4, n |= I(4); \ + if(v & I(0x0c)) v >>= 2, n |= I(2); \ + if(v & I(0x02)) v >>= 1, n |= I(1); \ + return n + +#define _c4_msb16_fallback \ + unsigned n = 0; \ + if(v & I(0xff00)) v >>= 8, n |= I(8); \ + if(v & I(0x00f0)) v >>= 4, n |= I(4); \ + if(v & I(0x000c)) v >>= 2, n |= I(2); \ + if(v & I(0x0002)) v >>= 1, n |= I(1); \ + return n + +#define _c4_msb32_fallback \ + unsigned n = 0; \ + if(v & I(0xffff0000)) v >>= 16, n |= 16; \ + if(v & I(0x0000ff00)) v >>= 8, n |= 8; \ + if(v & I(0x000000f0)) v >>= 4, n |= 4; \ + if(v & I(0x0000000c)) v >>= 2, n |= 2; \ + if(v & I(0x00000002)) v >>= 1, n |= 1; \ + return n + +#define _c4_msb64_fallback \ + unsigned n = 0; \ + if(v & I(0xffffffff00000000)) v >>= 32, n |= I(32); \ + if(v & I(0x00000000ffff0000)) v >>= 16, n |= I(16); \ + if(v & I(0x000000000000ff00)) v >>= 8, n |= I(8); \ + if(v & I(0x00000000000000f0)) v >>= 4, n |= I(4); \ + if(v & I(0x000000000000000c)) v >>= 2, n |= I(2); \ + if(v & I(0x0000000000000002)) v >>= 1, n |= I(1); \ + return n + + +// u8 +template<class I> +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 1u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clz) + // upcast to use the intrinsic, it's cheaper. + // Then remember that the upcast makes it to 31bits + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse(&bit, (unsigned long)v); + return bit; + #else + _c4_msb8_fallback; + #endif + #else + return 31u - (unsigned)__builtin_clz((unsigned)v); + #endif + #else + _c4_msb8_fallback; + #endif +} + +// u16 +template<class I> +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 2u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clz) + // upcast to use the intrinsic, it's cheaper. + // Then remember that the upcast makes it to 31bits + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse(&bit, (unsigned long)v); + return bit; + #else + _c4_msb16_fallback; + #endif + #else + return 31u - (unsigned)__builtin_clz((unsigned)v); + #endif + #else + _c4_msb16_fallback; + #endif +} + +// u32 +template<class I> +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 4u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clz) + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse(&bit, v); + return bit; + #else + _c4_msb32_fallback; + #endif + #else + return 31u - (unsigned)__builtin_clz((unsigned)v); + #endif + #else + _c4_msb32_fallback; + #endif +} + +// u64 in 64bits +template<class I> +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long) == 8u, unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clzl) + #ifdef C4_MSVC + #if !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse64(&bit, v); + return bit; + #else + _c4_msb64_fallback; + #endif + #else + return 63u - (unsigned)__builtin_clzl((unsigned long)v); + #endif + #else + _c4_msb64_fallback; + #endif +} + +// u64 in 32bits +template<class I> +C4_CONSTEXPR14 +auto msb(I v) noexcept + -> typename std::enable_if<sizeof(I) == 8u && sizeof(unsigned long long) == 8u && sizeof(unsigned long) != sizeof(unsigned long long), unsigned>::type +{ + C4_STATIC_ASSERT(std::is_unsigned<I>::value); + C4_ASSERT(v != 0); + #if _C4_USE_MSB_INTRINSIC(__builtin_clzll) + #ifdef C4_MSVC + #if !defined(C4_CPU_X86) && !defined(C4_CPU_ARM64) && !defined(C4_CPU_ARM) + unsigned long bit; + _BitScanReverse64(&bit, v); + return bit; + #else + _c4_msb64_fallback; + #endif + #else + return 63u - (unsigned)__builtin_clzll((unsigned long long)v); + #endif + #else + _c4_msb64_fallback; + #endif +} + +#undef _c4_msb8_fallback +#undef _c4_msb16_fallback +#undef _c4_msb32_fallback +#undef _c4_msb64_fallback + +/** @} */ + + +namespace detail { +template<class I, I val, I num_bits, bool finished> struct _msb11; +template<class I, I val, I num_bits> +struct _msb11< I, val, num_bits, false> +{ + enum : unsigned { num = _msb11<I, (val>>1), num_bits+I(1), ((val>>1)==I(0))>::num }; +}; +template<class I, I val, I num_bits> +struct _msb11<I, val, num_bits, true> +{ + static_assert(val == 0, "bad implementation"); + enum : unsigned { num = (unsigned)(num_bits-1) }; +}; +} // namespace detail + + +/** TMP version of msb(); this needs to be implemented with template + * meta-programming because C++11 cannot use a constexpr function with + * local variables + * @see msb */ +template<class I, I number> +struct msb11 +{ + enum : unsigned { value = detail::_msb11<I, number, 0, (number==I(0))>::num }; +}; + + + +#undef _C4_USE_LSB_INTRINSIC +#undef _C4_USE_MSB_INTRINSIC + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +// there is an implicit conversion below; it happens when E or B are +// narrower than int, and thus any operation will upcast the result to +// int, and then downcast to assign +C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wconversion") + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + if(exponent >= 0) + { + for(E e = 0; e < exponent; ++e) + r *= base; + } + else + { + exponent *= E(-1); + for(E e = 0; e < exponent; ++e) + r /= base; + } + return r; +} + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, B base, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + if(exponent >= 0) + { + for(E e = 0; e < exponent; ++e) + r *= base; + } + else + { + exponent *= E(-1); + for(E e = 0; e < exponent; ++e) + r /= base; + } + return r; +} + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, class Base, Base base, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + B bbase = B(base); + if(exponent >= 0) + { + for(E e = 0; e < exponent; ++e) + r *= bbase; + } + else + { + exponent *= E(-1); + for(E e = 0; e < exponent; ++e) + r /= bbase; + } + return r; +} + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(B base, E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + for(E e = 0; e < exponent; ++e) + r *= base; + return r; +} + +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, B base, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + for(E e = 0; e < exponent; ++e) + r *= base; + return r; +} +/** integer power; this function is constexpr-14 because of the local + * variables */ +template<class B, class Base, Base base, class E> +C4_CONSTEXPR14 C4_CONST auto ipow(E exponent) noexcept -> typename std::enable_if<!std::is_signed<E>::value, B>::type +{ + C4_STATIC_ASSERT(std::is_integral<E>::value); + B r = B(1); + B bbase = B(base); + for(E e = 0; e < exponent; ++e) + r *= bbase; + return r; +} + +C4_SUPPRESS_WARNING_GCC_CLANG_POP + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** return a mask with all bits set [first_bit,last_bit[; this function + * is constexpr-14 because of the local variables */ +template<class I> +C4_CONSTEXPR14 I contiguous_mask(I first_bit, I last_bit) +{ + I r = 0; + for(I i = first_bit; i < last_bit; ++i) + { + r |= (I(1) << i); + } + return r; +} + + +namespace detail { + +template<class I, I val, I first, I last, bool finished> +struct _ctgmsk11; + +template<class I, I val, I first, I last> +struct _ctgmsk11< I, val, first, last, true> +{ + enum : I { value = _ctgmsk11<I, val|(I(1)<<first), first+I(1), last, (first+1!=last)>::value }; +}; + +template<class I, I val, I first, I last> +struct _ctgmsk11< I, val, first, last, false> +{ + enum : I { value = val }; +}; + +} // namespace detail + + +/** TMP version of contiguous_mask(); this needs to be implemented with template + * meta-programming because C++11 cannot use a constexpr function with + * local variables + * @see contiguous_mask */ +template<class I, I first_bit, I last_bit> +struct contiguous_mask11 +{ + enum : I { value = detail::_ctgmsk11<I, I(0), first_bit, last_bit, (first_bit!=last_bit)>::value }; +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** use Empty Base Class Optimization to reduce the size of a pair of + * potentially empty types*/ + +namespace detail { +typedef enum { + tpc_same, + tpc_same_empty, + tpc_both_empty, + tpc_first_empty, + tpc_second_empty, + tpc_general +} TightPairCase_e; + +template<class First, class Second> +constexpr TightPairCase_e tpc_which_case() +{ + return std::is_same<First, Second>::value ? + std::is_empty<First>::value ? + tpc_same_empty + : + tpc_same + : + std::is_empty<First>::value && std::is_empty<Second>::value ? + tpc_both_empty + : + std::is_empty<First>::value ? + tpc_first_empty + : + std::is_empty<Second>::value ? + tpc_second_empty + : + tpc_general + ; +} + +template<class First, class Second, TightPairCase_e Case> +struct tight_pair +{ +private: + + First m_first; + Second m_second; + +public: + + using first_type = First; + using second_type = Second; + + tight_pair() : m_first(), m_second() {} + tight_pair(First const& f, Second const& s) : m_first(f), m_second(s) {} + + C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; } + C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; } +}; + +template<class First, class Second> +struct tight_pair<First, Second, tpc_same_empty> : public First +{ + static_assert(std::is_same<First, Second>::value, "bad implementation"); + + using first_type = First; + using second_type = Second; + + tight_pair() : First() {} + tight_pair(First const& f, Second const& /*s*/) : First(f) {} + + C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return reinterpret_cast<Second &>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return reinterpret_cast<Second const&>(*this); } +}; + +template<class First, class Second> +struct tight_pair<First, Second, tpc_both_empty> : public First, public Second +{ + using first_type = First; + using second_type = Second; + + tight_pair() : First(), Second() {} + tight_pair(First const& f, Second const& s) : First(f), Second(s) {} + + C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); } +}; + +template<class First, class Second> +struct tight_pair<First, Second, tpc_same> : public First +{ + Second m_second; + + using first_type = First; + using second_type = Second; + + tight_pair() : First() {} + tight_pair(First const& f, Second const& s) : First(f), m_second(s) {} + + C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; } +}; + +template<class First, class Second> +struct tight_pair<First, Second, tpc_first_empty> : public First +{ + Second m_second; + + using first_type = First; + using second_type = Second; + + tight_pair() : First(), m_second() {} + tight_pair(First const& f, Second const& s) : First(f), m_second(s) {} + + C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return m_second; } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return m_second; } +}; + +template<class First, class Second> +struct tight_pair<First, Second, tpc_second_empty> : public Second +{ + First m_first; + + using first_type = First; + using second_type = Second; + + tight_pair() : Second(), m_first() {} + tight_pair(First const& f, Second const& s) : Second(s), m_first(f) {} + + C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return m_first; } + C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return m_first; } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return static_cast<Second &>(*this); } + C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return static_cast<Second const&>(*this); } +}; + +} // namespace detail + +template<class First, class Second> +using tight_pair = detail::tight_pair<First, Second, detail::tpc_which_case<First,Second>()>; + +} // namespace c4 + +#endif /* _C4_MEMORY_UTIL_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/platform.hpp b/thirdparty/ryml/ext/c4core/src/c4/platform.hpp new file mode 100644 index 000000000..8f9a61f0f --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/platform.hpp @@ -0,0 +1,44 @@ +#ifndef _C4_PLATFORM_HPP_ +#define _C4_PLATFORM_HPP_ + +/** @file platform.hpp Provides platform information macros + * @ingroup basic_headers */ + +// see also https://sourceforge.net/p/predef/wiki/OperatingSystems/ + +#if defined(_WIN64) +# define C4_WIN +# define C4_WIN64 +#elif defined(_WIN32) +# define C4_WIN +# define C4_WIN32 +#elif defined(__ANDROID__) +# define C4_ANDROID +#elif defined(__APPLE__) +# include "TargetConditionals.h" +# if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR +# define C4_IOS +# elif TARGET_OS_MAC || TARGET_OS_OSX +# define C4_MACOS +# else +# error "Unknown Apple platform" +# endif +#elif defined(__linux__) || defined(__linux) +# define C4_UNIX +# define C4_LINUX +#elif defined(__unix__) || defined(__unix) +# define C4_UNIX +#elif defined(__arm__) || defined(__aarch64__) +# define C4_ARM +#elif defined(SWIG) +# define C4_SWIG +#else +# error "unknown platform" +#endif + +#if defined(__posix) || defined(C4_UNIX) || defined(C4_LINUX) +# define C4_POSIX +#endif + + +#endif /* _C4_PLATFORM_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/preprocessor.hpp b/thirdparty/ryml/ext/c4core/src/c4/preprocessor.hpp new file mode 100644 index 000000000..a55482540 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/preprocessor.hpp @@ -0,0 +1,123 @@ +#ifndef _C4_PREPROCESSOR_HPP_ +#define _C4_PREPROCESSOR_HPP_ + +/** @file preprocessor.hpp Contains basic macros and preprocessor utilities. + * @ingroup basic_headers */ + +#ifdef __clang__ + /* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to + * variadic macros is not portable, but works in clang, gcc, msvc, icc. + * clang requires switching off compiler warnings for pedantic mode. + * @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension +#elif defined(__GNUC__) + /* GCC also issues a warning for zero-args calls to variadic macros. + * This warning is switched on with -pedantic and apparently there is no + * easy way to turn it off as with clang. But marking this as a system + * header works. + * @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html + * @see http://stackoverflow.com/questions/35587137/ */ +# pragma GCC system_header +#endif + +#define C4_WIDEN(str) L"" str + +#define C4_COUNTOF(arr) (sizeof(arr)/sizeof((arr)[0])) + +#define C4_EXPAND(arg) arg + +/** useful in some macro calls with template arguments */ +#define C4_COMMA , +/** useful in some macro calls with template arguments + * @see C4_COMMA */ +#define C4_COMMA_X C4_COMMA + +/** expand and quote */ +#define C4_XQUOTE(arg) _C4_XQUOTE(arg) +#define _C4_XQUOTE(arg) C4_QUOTE(arg) +#define C4_QUOTE(arg) #arg + +/** expand and concatenate */ +#define C4_XCAT(arg1, arg2) _C4_XCAT(arg1, arg2) +#define _C4_XCAT(arg1, arg2) C4_CAT(arg1, arg2) +#define C4_CAT(arg1, arg2) arg1##arg2 + +#define C4_VERSION_CAT(major, minor, patch) ((major)*10000 + (minor)*100 + (patch)) + +/** A preprocessor foreach. Spectacular trick taken from: + * http://stackoverflow.com/a/1872506/5875572 + * The first argument is for a macro receiving a single argument, + * which will be called with every subsequent argument. There is + * currently a limit of 32 arguments, and at least 1 must be provided. + * +Example: +@code{.cpp} +struct Example { + int a; + int b; + int c; +}; +// define a one-arg macro to be called +#define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(Example, field) +#define PRN_STRUCT_OFFSETS_(structure, field) printf(C4_XQUOTE(structure) ":" C4_XQUOTE(field)" - offset=%zu\n", offsetof(structure, field)); + +// now call the macro for a, b and c +C4_FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); +@endcode */ +#define C4_FOR_EACH(what, ...) C4_FOR_EACH_SEP(what, ;, __VA_ARGS__) + +/** same as C4_FOR_EACH(), but use a custom separator between statements. + * If a comma is needed as the separator, use the C4_COMMA macro. + * @see C4_FOR_EACH + * @see C4_COMMA + */ +#define C4_FOR_EACH_SEP(what, sep, ...) _C4_FOR_EACH_(_C4_FOR_EACH_NARG(__VA_ARGS__), what, sep, __VA_ARGS__) + +/// @cond dev + +#define _C4_FOR_EACH_01(what, sep, x) what(x) sep +#define _C4_FOR_EACH_02(what, sep, x, ...) what(x) sep _C4_FOR_EACH_01(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_03(what, sep, x, ...) what(x) sep _C4_FOR_EACH_02(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_04(what, sep, x, ...) what(x) sep _C4_FOR_EACH_03(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_05(what, sep, x, ...) what(x) sep _C4_FOR_EACH_04(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_06(what, sep, x, ...) what(x) sep _C4_FOR_EACH_05(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_07(what, sep, x, ...) what(x) sep _C4_FOR_EACH_06(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_08(what, sep, x, ...) what(x) sep _C4_FOR_EACH_07(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_09(what, sep, x, ...) what(x) sep _C4_FOR_EACH_08(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_10(what, sep, x, ...) what(x) sep _C4_FOR_EACH_09(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_11(what, sep, x, ...) what(x) sep _C4_FOR_EACH_10(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_12(what, sep, x, ...) what(x) sep _C4_FOR_EACH_11(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_13(what, sep, x, ...) what(x) sep _C4_FOR_EACH_12(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_14(what, sep, x, ...) what(x) sep _C4_FOR_EACH_13(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_15(what, sep, x, ...) what(x) sep _C4_FOR_EACH_14(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_16(what, sep, x, ...) what(x) sep _C4_FOR_EACH_15(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_17(what, sep, x, ...) what(x) sep _C4_FOR_EACH_16(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_18(what, sep, x, ...) what(x) sep _C4_FOR_EACH_17(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_19(what, sep, x, ...) what(x) sep _C4_FOR_EACH_18(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_20(what, sep, x, ...) what(x) sep _C4_FOR_EACH_19(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_21(what, sep, x, ...) what(x) sep _C4_FOR_EACH_20(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_22(what, sep, x, ...) what(x) sep _C4_FOR_EACH_21(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_23(what, sep, x, ...) what(x) sep _C4_FOR_EACH_22(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_24(what, sep, x, ...) what(x) sep _C4_FOR_EACH_23(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_25(what, sep, x, ...) what(x) sep _C4_FOR_EACH_24(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_26(what, sep, x, ...) what(x) sep _C4_FOR_EACH_25(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_27(what, sep, x, ...) what(x) sep _C4_FOR_EACH_26(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_28(what, sep, x, ...) what(x) sep _C4_FOR_EACH_27(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_29(what, sep, x, ...) what(x) sep _C4_FOR_EACH_28(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_30(what, sep, x, ...) what(x) sep _C4_FOR_EACH_29(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_31(what, sep, x, ...) what(x) sep _C4_FOR_EACH_30(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_32(what, sep, x, ...) what(x) sep _C4_FOR_EACH_31(what, sep, __VA_ARGS__) +#define _C4_FOR_EACH_NARG(...) _C4_FOR_EACH_NARG_(__VA_ARGS__, _C4_FOR_EACH_RSEQ_N()) +#define _C4_FOR_EACH_NARG_(...) _C4_FOR_EACH_ARG_N(__VA_ARGS__) +#define _C4_FOR_EACH_ARG_N(_01, _02, _03, _04, _05, _06, _07, _08, _09, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, N, ...) N +#define _C4_FOR_EACH_RSEQ_N() 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01 +#define _C4_FOR_EACH_(N, what, sep, ...) C4_XCAT(_C4_FOR_EACH_, N)(what, sep, __VA_ARGS__) + +/// @endcond + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#endif /* _C4_PREPROCESSOR_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/restrict.hpp b/thirdparty/ryml/ext/c4core/src/c4/restrict.hpp new file mode 100644 index 000000000..a8a59301b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/restrict.hpp @@ -0,0 +1,51 @@ +#ifndef _C4_RESTRICT_HPP_ +#define _C4_RESTRICT_HPP_ + +/** @file restrict.hpp macros defining shorthand symbols for restricted + * pointers and references + * @see unrestrict.hpp + * @see restrict + */ + +/** @defgroup restrict Restrict utilities + * macros defining shorthand symbols for restricted + * pointers and references + * ```cpp + * void sum_arrays(size_t sz, float const* C4_RESTRICT a, float const *C4_RESTRICT b, float *result); + * float * C4_RESTRICT ptr; + * float & C4_RESTRICT ref = *ptr; + * float const* C4_RESTRICT cptr; + * float const& C4_RESTRICT cref = *cptr; + * + * // becomes this: + * #include <c4/restrict.hpp> + * void sum_arrays(size_t sz, float c$ a, float c$ b, float * result); + * float $ ptr; + * float $$ ref = *ptr; + * float c$ cptr; + * float c$$ cref = *cptr; + * ``` + * @ingroup types + * @{ */ + +/** @def \$ a restricted pointer */ +/** @def c\$ a restricted pointer to const data */ + +/** @def \$\$ a restricted reference */ +/** @def c\$\$ a restricted reference to const data */ + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdollar-in-identifier-extension" +#elif defined(__GNUC__) +#endif + +#define $ * C4_RESTRICT // a restricted pointer +#define c$ const* C4_RESTRICT // a restricted pointer to const data + +#define $$ & C4_RESTRICT // restricted reference +#define c$$ const& C4_RESTRICT // restricted reference to const data + +/** @} */ + +#endif /* _C4_RESTRICT_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/span.hpp b/thirdparty/ryml/ext/c4core/src/c4/span.hpp new file mode 100644 index 000000000..cc6e463dc --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/span.hpp @@ -0,0 +1,517 @@ +#ifndef _C4_SPAN_HPP_ +#define _C4_SPAN_HPP_ + +/** @file span.hpp Provides span classes. */ + +#include "c4/config.hpp" +#include "c4/error.hpp" +#include "c4/szconv.hpp" + +#include <algorithm> + +namespace c4 { + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** a crtp base for implementing span classes + * + * A span is a non-owning range of elements contiguously stored in memory. + * Unlike STL's array_view, the span allows write-access to its members. + * + * To obtain subspans from a span, the following const member functions + * are available: + * - subspan(first, num) + * - range(first, last) + * - first(num) + * - last(num) + * + * A span can also be resized via the following non-const member functions: + * - resize(sz) + * - ltrim(num) + * - rtrim(num) + * + * @see span + * @see cspan + * @see spanrs + * @see cspanrs + * @see spanrsl + * @see cspanrsl + */ +template<class T, class I, class SpanImpl> +class span_crtp +{ +// some utility defines, undefined at the end of this class +#define _c4this ((SpanImpl *)this) +#define _c4cthis ((SpanImpl const*)this) +#define _c4ptr ((SpanImpl *)this)->m_ptr +#define _c4cptr ((SpanImpl const*)this)->m_ptr +#define _c4sz ((SpanImpl *)this)->m_size +#define _c4csz ((SpanImpl const*)this)->m_size + +public: + + _c4_DEFINE_ARRAY_TYPES(T, I); + +public: + + C4_ALWAYS_INLINE constexpr I value_size() const noexcept { return sizeof(T); } + C4_ALWAYS_INLINE constexpr I elm_size () const noexcept { return sizeof(T); } + C4_ALWAYS_INLINE constexpr I type_size () const noexcept { return sizeof(T); } + C4_ALWAYS_INLINE I byte_size () const noexcept { return _c4csz*sizeof(T); } + + C4_ALWAYS_INLINE bool empty() const noexcept { return _c4csz == 0; } + C4_ALWAYS_INLINE I size() const noexcept { return _c4csz; } + //C4_ALWAYS_INLINE I capacity() const noexcept { return _c4sz; } // this must be defined by impl classes + + C4_ALWAYS_INLINE void clear() noexcept { _c4sz = 0; } + + C4_ALWAYS_INLINE T * data() noexcept { return _c4ptr; } + C4_ALWAYS_INLINE T const* data() const noexcept { return _c4cptr; } + + C4_ALWAYS_INLINE iterator begin() noexcept { return _c4ptr; } + C4_ALWAYS_INLINE const_iterator begin() const noexcept { return _c4cptr; } + C4_ALWAYS_INLINE const_iterator cbegin() const noexcept { return _c4cptr; } + + C4_ALWAYS_INLINE iterator end() noexcept { return _c4ptr + _c4sz; } + C4_ALWAYS_INLINE const_iterator end() const noexcept { return _c4cptr + _c4csz; } + C4_ALWAYS_INLINE const_iterator cend() const noexcept { return _c4cptr + _c4csz; } + + C4_ALWAYS_INLINE reverse_iterator rbegin() noexcept { return reverse_iterator(_c4ptr + _c4sz); } + C4_ALWAYS_INLINE const_reverse_iterator rbegin() const noexcept { return reverse_iterator(_c4cptr + _c4sz); } + C4_ALWAYS_INLINE const_reverse_iterator crbegin() const noexcept { return reverse_iterator(_c4cptr + _c4sz); } + + C4_ALWAYS_INLINE reverse_iterator rend() noexcept { return const_reverse_iterator(_c4ptr); } + C4_ALWAYS_INLINE const_reverse_iterator rend() const noexcept { return const_reverse_iterator(_c4cptr); } + C4_ALWAYS_INLINE const_reverse_iterator crend() const noexcept { return const_reverse_iterator(_c4cptr); } + + C4_ALWAYS_INLINE T & front() C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4ptr [0]; } + C4_ALWAYS_INLINE T const& front() const C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4cptr[0]; } + + C4_ALWAYS_INLINE T & back() C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4ptr [_c4sz - 1]; } + C4_ALWAYS_INLINE T const& back() const C4_NOEXCEPT_X { C4_XASSERT(!empty()); return _c4cptr[_c4csz - 1]; } + + C4_ALWAYS_INLINE T & operator[] (I i) C4_NOEXCEPT_X { C4_XASSERT(i >= 0 && i < _c4sz ); return _c4ptr [i]; } + C4_ALWAYS_INLINE T const& operator[] (I i) const C4_NOEXCEPT_X { C4_XASSERT(i >= 0 && i < _c4csz); return _c4cptr[i]; } + + C4_ALWAYS_INLINE SpanImpl subspan(I first, I num) const C4_NOEXCEPT_X + { + C4_XASSERT((first >= 0 && first < _c4csz) || (first == _c4csz && num == 0)); + C4_XASSERT((first + num >= 0) && (first + num <= _c4csz)); + return _c4cthis->_select(_c4cptr + first, num); + } + C4_ALWAYS_INLINE SpanImpl subspan(I first) const C4_NOEXCEPT_X ///< goes up until the end of the span + { + C4_XASSERT(first >= 0 && first <= _c4csz); + return _c4cthis->_select(_c4cptr + first, _c4csz - first); + } + + C4_ALWAYS_INLINE SpanImpl range(I first, I last) const C4_NOEXCEPT_X ///< last element is NOT included + { + C4_XASSERT(((first >= 0) && (first < _c4csz)) || (first == _c4csz && first == last)); + C4_XASSERT((last >= 0) && (last <= _c4csz)); + C4_XASSERT(last >= first); + return _c4cthis->_select(_c4cptr + first, last - first); + } + C4_ALWAYS_INLINE SpanImpl range(I first) const C4_NOEXCEPT_X ///< goes up until the end of the span + { + C4_XASSERT(((first >= 0) && (first <= _c4csz))); + return _c4cthis->_select(_c4cptr + first, _c4csz - first); + } + + C4_ALWAYS_INLINE SpanImpl first(I num) const C4_NOEXCEPT_X ///< get the first num elements, starting at 0 + { + C4_XASSERT((num >= 0) && (num <= _c4csz)); + return _c4cthis->_select(_c4cptr, num); + } + C4_ALWAYS_INLINE SpanImpl last(I num) const C4_NOEXCEPT_X ///< get the last num elements, starting at size()-num + { + C4_XASSERT((num >= 0) && (num <= _c4csz)); + return _c4cthis->_select(_c4cptr + _c4csz - num, num); + } + + bool is_subspan(span_crtp const& ss) const noexcept + { + if(_c4cptr == nullptr) return false; + auto *b = begin(), *e = end(); + auto *ssb = ss.begin(), *sse = ss.end(); + if(ssb >= b && sse <= e) + { + return true; + } + else + { + return false; + } + } + + /** COMPLement Left: return the complement to the left of the beginning of the given subspan. + * If ss does not begin inside this, returns an empty substring. */ + SpanImpl compll(span_crtp const& ss) const C4_NOEXCEPT_X + { + auto ssb = ss.begin(); + auto b = begin(); + auto e = end(); + if(ssb >= b && ssb <= e) + { + return subspan(0, static_cast<size_t>(ssb - b)); + } + else + { + return subspan(0, 0); + } + } + + /** COMPLement Right: return the complement to the right of the end of the given subspan. + * If ss does not end inside this, returns an empty substring. */ + SpanImpl complr(span_crtp const& ss) const C4_NOEXCEPT_X + { + auto sse = ss.end(); + auto b = begin(); + auto e = end(); + if(sse >= b && sse <= e) + { + return subspan(static_cast<size_t>(sse - b), static_cast<size_t>(e - sse)); + } + else + { + return subspan(0, 0); + } + } + + C4_ALWAYS_INLINE bool same_span(span_crtp const& that) const noexcept + { + return size() == that.size() && data() == that.data(); + } + template<class I2, class Impl2> + C4_ALWAYS_INLINE bool same_span(span_crtp<T, I2, Impl2> const& that) const C4_NOEXCEPT_X + { + I tsz = szconv<I>(that.size()); // x-asserts that the size does not overflow + return size() == tsz && data() == that.data(); + } + +#undef _c4this +#undef _c4cthis +#undef _c4ptr +#undef _c4cptr +#undef _c4sz +#undef _c4csz +}; + +//----------------------------------------------------------------------------- +template<class T, class Il, class Ir, class _Impll, class _Implr> +inline constexpr bool operator== +( + span_crtp<T, Il, _Impll> const& l, + span_crtp<T, Ir, _Implr> const& r +) +{ +#if C4_CPP >= 14 + return std::equal(l.begin(), l.end(), r.begin(), r.end()); +#else + return l.same_span(r) || std::equal(l.begin(), l.end(), r.begin()); +#endif +} + +template<class T, class Il, class Ir, class _Impll, class _Implr> +inline constexpr bool operator!= +( + span_crtp<T, Il, _Impll> const& l, + span_crtp<T, Ir, _Implr> const& r +) +{ + return ! (l == r); +} + +//----------------------------------------------------------------------------- +template<class T, class Il, class Ir, class _Impll, class _Implr> +inline constexpr bool operator< +( + span_crtp<T, Il, _Impll> const& l, + span_crtp<T, Ir, _Implr> const& r +) +{ + return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end()); +} + +template<class T, class Il, class Ir, class _Impll, class _Implr> +inline constexpr bool operator<= +( + span_crtp<T, Il, _Impll> const& l, + span_crtp<T, Ir, _Implr> const& r +) +{ + return ! (l > r); +} + +//----------------------------------------------------------------------------- +template<class T, class Il, class Ir, class _Impll, class _Implr> +inline constexpr bool operator> +( + span_crtp<T, Il, _Impll> const& l, + span_crtp<T, Ir, _Implr> const& r +) +{ + return r < l; +} + +//----------------------------------------------------------------------------- +template<class T, class Il, class Ir, class _Impll, class _Implr> +inline constexpr bool operator>= +( + span_crtp<T, Il, _Impll> const& l, + span_crtp<T, Ir, _Implr> const& r +) +{ + return ! (l < r); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** A non-owning span of elements contiguously stored in memory. */ +template<class T, class I=C4_SIZE_TYPE> +class span : public span_crtp<T, I, span<T, I>> +{ + friend class span_crtp<T, I, span<T, I>>; + + T * C4_RESTRICT m_ptr; + I m_size; + + C4_ALWAYS_INLINE span _select(T *p, I sz) const { return span(p, sz); } + +public: + + _c4_DEFINE_ARRAY_TYPES(T, I); + using NCT = typename std::remove_const<T>::type; //!< NCT=non const type + using CT = typename std::add_const<T>::type; //!< CT=const type + using const_type = span<CT, I>; + + /// convert automatically to span of const T + operator span<CT, I> () const { span<CT, I> s(m_ptr, m_size); return s; } + +public: + + C4_ALWAYS_INLINE C4_CONSTEXPR14 span() noexcept : m_ptr{nullptr}, m_size{0} {} + + span(span const&) = default; + span(span &&) = default; + + span& operator= (span const&) = default; + span& operator= (span &&) = default; + +public: + + /** @name Construction and assignment from same type */ + /** @{ */ + + template<size_t N> C4_ALWAYS_INLINE C4_CONSTEXPR14 span (T (&arr)[N]) noexcept : m_ptr{arr}, m_size{N} {} + template<size_t N> C4_ALWAYS_INLINE C4_CONSTEXPR14 void assign(T (&arr)[N]) noexcept { m_ptr = arr; m_size = N; } + + C4_ALWAYS_INLINE C4_CONSTEXPR14 span(T *p, I sz) noexcept : m_ptr{p}, m_size{sz} {} + C4_ALWAYS_INLINE C4_CONSTEXPR14 void assign(T *p, I sz) noexcept { m_ptr = p; m_size = sz; } + + C4_ALWAYS_INLINE C4_CONSTEXPR14 span (c4::aggregate_t, std::initializer_list<T> il) noexcept : m_ptr{&*il.begin()}, m_size{il.size()} {} + C4_ALWAYS_INLINE C4_CONSTEXPR14 void assign(c4::aggregate_t, std::initializer_list<T> il) noexcept { m_ptr = &*il.begin(); m_size = il.size(); } + + /** @} */ + +public: + + C4_ALWAYS_INLINE I capacity() const noexcept { return m_size; } + + C4_ALWAYS_INLINE void resize(I sz) C4_NOEXCEPT_A { C4_ASSERT(sz <= m_size); m_size = sz; } + C4_ALWAYS_INLINE void rtrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; } + C4_ALWAYS_INLINE void ltrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; m_ptr += n; } + +}; +template<class T, class I=C4_SIZE_TYPE> using cspan = span<const T, I>; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** A non-owning span resizeable up to a capacity. Subselection or resizing + * will keep the original provided it starts at begin(). If subselection or + * resizing change the pointer, then the original capacity information will + * be lost. + * + * Thus, resizing via resize() and ltrim() and subselecting via first() + * or any of subspan() or range() when starting from the beginning will keep + * the original capacity. OTOH, using last(), or any of subspan() or range() + * with an offset from the start will remove from capacity (shifting the + * pointer) by the corresponding offset. If this is undesired, then consider + * using spanrsl. + * + * @see spanrs for a span resizeable on the right + * @see spanrsl for a span resizeable on the right and left + */ + +template<class T, class I=C4_SIZE_TYPE> +class spanrs : public span_crtp<T, I, spanrs<T, I>> +{ + friend class span_crtp<T, I, spanrs<T, I>>; + + T * C4_RESTRICT m_ptr; + I m_size; + I m_capacity; + + C4_ALWAYS_INLINE spanrs _select(T *p, I sz) const noexcept + { + C4_ASSERT(p >= m_ptr); + size_t delta = static_cast<size_t>(p - m_ptr); + C4_ASSERT(m_capacity >= delta); + return spanrs(p, sz, static_cast<size_t>(m_capacity - delta)); + } + +public: + + _c4_DEFINE_ARRAY_TYPES(T, I); + using NCT = typename std::remove_const<T>::type; //!< NCT=non const type + using CT = typename std::add_const<T>::type; //!< CT=const type + using const_type = spanrs<CT, I>; + + /// convert automatically to span of T + C4_ALWAYS_INLINE operator span<T, I > () const noexcept { return span<T, I>(m_ptr, m_size); } + /// convert automatically to span of const T + //C4_ALWAYS_INLINE operator span<CT, I> () const noexcept { span<CT, I> s(m_ptr, m_size); return s; } + /// convert automatically to spanrs of const T + C4_ALWAYS_INLINE operator spanrs<CT, I> () const noexcept { spanrs<CT, I> s(m_ptr, m_size, m_capacity); return s; } + +public: + + C4_ALWAYS_INLINE spanrs() noexcept : m_ptr{nullptr}, m_size{0}, m_capacity{0} {} + + spanrs(spanrs const&) = default; + spanrs(spanrs &&) = default; + + spanrs& operator= (spanrs const&) = default; + spanrs& operator= (spanrs &&) = default; + +public: + + /** @name Construction and assignment from same type */ + /** @{ */ + + C4_ALWAYS_INLINE spanrs(T *p, I sz) noexcept : m_ptr{p}, m_size{sz}, m_capacity{sz} {} + /** @warning will reset the capacity to sz */ + C4_ALWAYS_INLINE void assign(T *p, I sz) noexcept { m_ptr = p; m_size = sz; m_capacity = sz; } + + C4_ALWAYS_INLINE spanrs(T *p, I sz, I cap) noexcept : m_ptr{p}, m_size{sz}, m_capacity{cap} {} + C4_ALWAYS_INLINE void assign(T *p, I sz, I cap) noexcept { m_ptr = p; m_size = sz; m_capacity = cap; } + + template<size_t N> C4_ALWAYS_INLINE spanrs(T (&arr)[N]) noexcept : m_ptr{arr}, m_size{N}, m_capacity{N} {} + template<size_t N> C4_ALWAYS_INLINE void assign(T (&arr)[N]) noexcept { m_ptr = arr; m_size = N; m_capacity = N; } + + C4_ALWAYS_INLINE spanrs(c4::aggregate_t, std::initializer_list<T> il) noexcept : m_ptr{il.begin()}, m_size{il.size()}, m_capacity{il.size()} {} + C4_ALWAYS_INLINE void assign(c4::aggregate_t, std::initializer_list<T> il) noexcept { m_ptr = il.begin(); m_size = il.size(); m_capacity = il.size(); } + + /** @} */ + +public: + + C4_ALWAYS_INLINE I capacity() const noexcept { return m_capacity; } + + C4_ALWAYS_INLINE void resize(I sz) C4_NOEXCEPT_A { C4_ASSERT(sz <= m_capacity); m_size = sz; } + C4_ALWAYS_INLINE void rtrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; } + C4_ALWAYS_INLINE void ltrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; m_ptr += n; m_capacity -= n; } + +}; +template<class T, class I=C4_SIZE_TYPE> using cspanrs = spanrs<const T, I>; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** A non-owning span which always retains the capacity of the original + * range it was taken from (though it may loose its original size). + * The resizing methods resize(), ltrim(), rtrim() as well + * as the subselection methods subspan(), range(), first() and last() can be + * used at will without loosing the original capacity; the full capacity span + * can always be recovered by calling original(). + */ +template<class T, class I=C4_SIZE_TYPE> +class spanrsl : public span_crtp<T, I, spanrsl<T, I>> +{ + friend class span_crtp<T, I, spanrsl<T, I>>; + + T *C4_RESTRICT m_ptr; ///< the current ptr. the original ptr is (m_ptr - m_offset). + I m_size; ///< the current size. the original size is unrecoverable. + I m_capacity; ///< the current capacity. the original capacity is (m_capacity + m_offset). + I m_offset; ///< the offset of the current m_ptr to the start of the original memory block. + + C4_ALWAYS_INLINE spanrsl _select(T *p, I sz) const noexcept + { + C4_ASSERT(p >= m_ptr); + I delta = static_cast<I>(p - m_ptr); + C4_ASSERT(m_capacity >= delta); + return spanrsl(p, sz, static_cast<I>(m_capacity - delta), m_offset + delta); + } + +public: + + _c4_DEFINE_ARRAY_TYPES(T, I); + using NCT = typename std::remove_const<T>::type; //!< NCT=non const type + using CT = typename std::add_const<T>::type; //!< CT=const type + using const_type = spanrsl<CT, I>; + + C4_ALWAYS_INLINE operator span<T, I> () const noexcept { return span<T, I>(m_ptr, m_size); } + C4_ALWAYS_INLINE operator spanrs<T, I> () const noexcept { return spanrs<T, I>(m_ptr, m_size, m_capacity); } + C4_ALWAYS_INLINE operator spanrsl<CT, I> () const noexcept { return spanrsl<CT, I>(m_ptr, m_size, m_capacity, m_offset); } + +public: + + C4_ALWAYS_INLINE spanrsl() noexcept : m_ptr{nullptr}, m_size{0}, m_capacity{0}, m_offset{0} {} + + spanrsl(spanrsl const&) = default; + spanrsl(spanrsl &&) = default; + + spanrsl& operator= (spanrsl const&) = default; + spanrsl& operator= (spanrsl &&) = default; + +public: + + C4_ALWAYS_INLINE spanrsl(T *p, I sz) noexcept : m_ptr{p}, m_size{sz}, m_capacity{sz}, m_offset{0} {} + C4_ALWAYS_INLINE void assign(T *p, I sz) noexcept { m_ptr = p; m_size = sz; m_capacity = sz; m_offset = 0; } + + C4_ALWAYS_INLINE spanrsl(T *p, I sz, I cap) noexcept : m_ptr{p}, m_size{sz}, m_capacity{cap}, m_offset{0} {} + C4_ALWAYS_INLINE void assign(T *p, I sz, I cap) noexcept { m_ptr = p; m_size = sz; m_capacity = cap; m_offset = 0; } + + C4_ALWAYS_INLINE spanrsl(T *p, I sz, I cap, I offs) noexcept : m_ptr{p}, m_size{sz}, m_capacity{cap}, m_offset{offs} {} + C4_ALWAYS_INLINE void assign(T *p, I sz, I cap, I offs) noexcept { m_ptr = p; m_size = sz; m_capacity = cap; m_offset = offs; } + + template<size_t N> C4_ALWAYS_INLINE spanrsl(T (&arr)[N]) noexcept : m_ptr{arr}, m_size{N}, m_capacity{N}, m_offset{0} {} + template<size_t N> C4_ALWAYS_INLINE void assign(T (&arr)[N]) noexcept { m_ptr = arr; m_size = N; m_capacity = N; m_offset = 0; } + + C4_ALWAYS_INLINE spanrsl(c4::aggregate_t, std::initializer_list<T> il) noexcept : m_ptr{il.begin()}, m_size{il.size()}, m_capacity{il.size()}, m_offset{0} {} + C4_ALWAYS_INLINE void assign (c4::aggregate_t, std::initializer_list<T> il) noexcept { m_ptr = il.begin(); m_size = il.size(); m_capacity = il.size(); m_offset = 0; } + +public: + + C4_ALWAYS_INLINE I offset() const noexcept { return m_offset; } + C4_ALWAYS_INLINE I capacity() const noexcept { return m_capacity; } + + C4_ALWAYS_INLINE void resize(I sz) C4_NOEXCEPT_A { C4_ASSERT(sz <= m_capacity); m_size = sz; } + C4_ALWAYS_INLINE void rtrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; } + C4_ALWAYS_INLINE void ltrim (I n ) C4_NOEXCEPT_A { C4_ASSERT(n >= 0 && n < m_size); m_size -= n; m_ptr += n; m_offset += n; m_capacity -= n; } + + /** recover the original span as an spanrsl */ + C4_ALWAYS_INLINE spanrsl original() const + { + return spanrsl(m_ptr - m_offset, m_capacity + m_offset, m_capacity + m_offset, 0); + } + /** recover the original span as a different span type. Example: spanrs<...> orig = s.original<spanrs>(); */ + template<template<class, class> class OtherSpanType> + C4_ALWAYS_INLINE OtherSpanType<T, I> original() + { + return OtherSpanType<T, I>(m_ptr - m_offset, m_capacity + m_offset); + } +}; +template<class T, class I=C4_SIZE_TYPE> using cspanrsl = spanrsl<const T, I>; + + +} // namespace c4 + + +#endif /* _C4_SPAN_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/std/std.hpp b/thirdparty/ryml/ext/c4core/src/c4/std/std.hpp new file mode 100644 index 000000000..3df8d1986 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/std/std.hpp @@ -0,0 +1,10 @@ +#ifndef _C4_STD_STD_HPP_ +#define _C4_STD_STD_HPP_ + +/** @file std.hpp includes all c4-std interop files */ + +#include "c4/std/vector.hpp" +#include "c4/std/string.hpp" +#include "c4/std/tuple.hpp" + +#endif // _C4_STD_STD_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/std/std_fwd.hpp b/thirdparty/ryml/ext/c4core/src/c4/std/std_fwd.hpp new file mode 100644 index 000000000..8c42ce711 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/std/std_fwd.hpp @@ -0,0 +1,10 @@ +#ifndef _C4_STD_STD_FWD_HPP_ +#define _C4_STD_STD_FWD_HPP_ + +/** @file std_fwd.hpp includes all c4-std interop fwd files */ + +#include "c4/std/vector_fwd.hpp" +#include "c4/std/string_fwd.hpp" +//#include "c4/std/tuple_fwd.hpp" + +#endif // _C4_STD_STD_FWD_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/std/string.hpp b/thirdparty/ryml/ext/c4core/src/c4/std/string.hpp new file mode 100644 index 000000000..5b9837ab4 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/std/string.hpp @@ -0,0 +1,97 @@ +#ifndef _C4_STD_STRING_HPP_ +#define _C4_STD_STRING_HPP_ + +/** @file string.hpp */ + +#ifndef C4CORE_SINGLE_HEADER +#include "c4/substr.hpp" +#endif + +#include <string> + +namespace c4 { + +//----------------------------------------------------------------------------- + +/** get a writeable view to an existing std::string. + * When the string is empty, the returned view will be pointing + * at the character with value '\0', but the size will be zero. + * @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at + */ +C4_ALWAYS_INLINE c4::substr to_substr(std::string &s) noexcept +{ + #if C4_CPP < 11 + #error this function will do undefined behavior + #endif + // since c++11 it is legal to call s[s.size()]. + return c4::substr(&s[0], s.size()); +} + +/** get a readonly view to an existing std::string. + * When the string is empty, the returned view will be pointing + * at the character with value '\0', but the size will be zero. + * @see https://en.cppreference.com/w/cpp/string/basic_string/operator_at + */ +C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string const& s) noexcept +{ + #if C4_CPP < 11 + #error this function will do undefined behavior + #endif + // since c++11 it is legal to call s[s.size()]. + return c4::csubstr(&s[0], s.size()); +} + +//----------------------------------------------------------------------------- + +C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) == 0; } +C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) != 0; } +C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) >= 0; } +C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) > 0; } +C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) <= 0; } +C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::string const& s) { return ss.compare(to_csubstr(s)) < 0; } + +C4_ALWAYS_INLINE bool operator== (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) == 0; } +C4_ALWAYS_INLINE bool operator!= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) != 0; } +C4_ALWAYS_INLINE bool operator>= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) <= 0; } +C4_ALWAYS_INLINE bool operator> (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) < 0; } +C4_ALWAYS_INLINE bool operator<= (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) >= 0; } +C4_ALWAYS_INLINE bool operator< (std::string const& s, c4::csubstr ss) { return ss.compare(to_csubstr(s)) > 0; } + +//----------------------------------------------------------------------------- + +/** copy an std::string to a writeable string view */ +inline size_t to_chars(c4::substr buf, std::string const& s) +{ + C4_ASSERT(!buf.overlaps(to_csubstr(s))); + size_t len = buf.len < s.size() ? buf.len : s.size(); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len) + { + C4_ASSERT(s.data() != nullptr); + C4_ASSERT(buf.str != nullptr); + memcpy(buf.str, s.data(), len); + } + return s.size(); // return the number of needed chars +} + +/** copy a string view to an existing std::string */ +inline bool from_chars(c4::csubstr buf, std::string * s) +{ + s->resize(buf.len); + C4_ASSERT(!buf.overlaps(to_csubstr(*s))); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(buf.len) + { + C4_ASSERT(buf.str != nullptr); + memcpy(&(*s)[0], buf.str, buf.len); + } + return true; +} + +} // namespace c4 + +#endif // _C4_STD_STRING_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/std/string_fwd.hpp b/thirdparty/ryml/ext/c4core/src/c4/std/string_fwd.hpp new file mode 100644 index 000000000..6f6f42146 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/std/string_fwd.hpp @@ -0,0 +1,56 @@ +#ifndef _C4_STD_STRING_FWD_HPP_ +#define _C4_STD_STRING_FWD_HPP_ + +/** @file string_fwd.hpp */ + +#ifndef DOXYGEN + +#ifndef C4CORE_SINGLE_HEADER +#include "c4/substr_fwd.hpp" +#endif + +#include <cstddef> + +// forward declarations for std::string +#if defined(__GLIBCXX__) || defined(__GLIBCPP__) +#include <bits/stringfwd.h> // use the fwd header in glibcxx +#elif defined(_LIBCPP_VERSION) || defined(__APPLE_CC__) +#include <iosfwd> // use the fwd header in stdlibc++ +#elif defined(_MSC_VER) +//! @todo is there a fwd header in msvc? +namespace std { +template<typename> struct char_traits; +template<typename> class allocator; +template<typename _CharT, typename _Traits, typename _Alloc> class basic_string; +using string = basic_string<char, char_traits<char>, allocator<char>>; +} /* namespace std */ +#else +#error "unknown standard library" +#endif + +namespace c4 { + +C4_ALWAYS_INLINE c4::substr to_substr(std::string &s) noexcept; +C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string const& s) noexcept; + +bool operator== (c4::csubstr ss, std::string const& s); +bool operator!= (c4::csubstr ss, std::string const& s); +bool operator>= (c4::csubstr ss, std::string const& s); +bool operator> (c4::csubstr ss, std::string const& s); +bool operator<= (c4::csubstr ss, std::string const& s); +bool operator< (c4::csubstr ss, std::string const& s); + +bool operator== (std::string const& s, c4::csubstr ss); +bool operator!= (std::string const& s, c4::csubstr ss); +bool operator>= (std::string const& s, c4::csubstr ss); +bool operator> (std::string const& s, c4::csubstr ss); +bool operator<= (std::string const& s, c4::csubstr ss); +bool operator< (std::string const& s, c4::csubstr ss); + +size_t to_chars(c4::substr buf, std::string const& s); +bool from_chars(c4::csubstr buf, std::string * s); + +} // namespace c4 + +#endif // DOXYGEN +#endif // _C4_STD_STRING_FWD_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/std/tuple.hpp b/thirdparty/ryml/ext/c4core/src/c4/std/tuple.hpp new file mode 100644 index 000000000..5edcd63da --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/std/tuple.hpp @@ -0,0 +1,184 @@ +#ifndef _C4_STD_TUPLE_HPP_ +#define _C4_STD_TUPLE_HPP_ + +/** @file tuple.hpp */ + +#ifndef C4CORE_SINGLE_HEADER +#include "c4/format.hpp" +#endif + +#include <tuple> + +/** this is a work in progress */ +#undef C4_TUPLE_TO_CHARS + +namespace c4 { + +#ifdef C4_TUPLE_TO_CHARS +namespace detail { + +template< size_t Curr, class... Types > +struct tuple_helper +{ + static size_t do_cat(substr buf, std::tuple< Types... > const& tp) + { + size_t num = to_chars(buf, std::get<Curr>(tp)); + buf = buf.len >= num ? buf.sub(num) : substr{}; + num += tuple_helper< Curr+1, Types... >::do_cat(buf, tp); + return num; + } + + static size_t do_uncat(csubstr buf, std::tuple< Types... > & tp) + { + size_t num = from_str_trim(buf, &std::get<Curr>(tp)); + if(num == csubstr::npos) return csubstr::npos; + buf = buf.len >= num ? buf.sub(num) : substr{}; + num += tuple_helper< Curr+1, Types... >::do_uncat(buf, tp); + return num; + } + + template< class Sep > + static size_t do_catsep_more(substr buf, Sep const& sep, std::tuple< Types... > const& tp) + { + size_t ret = to_chars(buf, sep), num = ret; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = to_chars(buf, std::get<Curr>(tp)); + num += ret; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = tuple_helper< Curr+1, Types... >::do_catsep_more(buf, sep, tp); + num += ret; + return num; + } + + template< class Sep > + static size_t do_uncatsep_more(csubstr buf, Sep & sep, std::tuple< Types... > & tp) + { + size_t ret = from_str_trim(buf, &sep), num = ret; + if(ret == csubstr::npos) return csubstr::npos; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = from_str_trim(buf, &std::get<Curr>(tp)); + if(ret == csubstr::npos) return csubstr::npos; + num += ret; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = tuple_helper< Curr+1, Types... >::do_uncatsep_more(buf, sep, tp); + if(ret == csubstr::npos) return csubstr::npos; + num += ret; + return num; + } + + static size_t do_format(substr buf, csubstr fmt, std::tuple< Types... > const& tp) + { + auto pos = fmt.find("{}"); + if(pos != csubstr::npos) + { + size_t num = to_chars(buf, fmt.sub(0, pos)); + size_t out = num; + buf = buf.len >= num ? buf.sub(num) : substr{}; + num = to_chars(buf, std::get<Curr>(tp)); + out += num; + buf = buf.len >= num ? buf.sub(num) : substr{}; + num = tuple_helper< Curr+1, Types... >::do_format(buf, fmt.sub(pos + 2), tp); + out += num; + return out; + } + else + { + return format(buf, fmt); + } + } + + static size_t do_unformat(csubstr buf, csubstr fmt, std::tuple< Types... > & tp) + { + auto pos = fmt.find("{}"); + if(pos != csubstr::npos) + { + size_t num = pos; + size_t out = num; + buf = buf.len >= num ? buf.sub(num) : substr{}; + num = from_str_trim(buf, &std::get<Curr>(tp)); + out += num; + buf = buf.len >= num ? buf.sub(num) : substr{}; + num = tuple_helper< Curr+1, Types... >::do_unformat(buf, fmt.sub(pos + 2), tp); + out += num; + return out; + } + else + { + return tuple_helper< sizeof...(Types), Types... >::do_unformat(buf, fmt, tp); + } + } + +}; + +/** @todo VS compilation fails for this class */ +template< class... Types > +struct tuple_helper< sizeof...(Types), Types... > +{ + static size_t do_cat(substr /*buf*/, std::tuple<Types...> const& /*tp*/) { return 0; } + static size_t do_uncat(csubstr /*buf*/, std::tuple<Types...> & /*tp*/) { return 0; } + + template< class Sep > static size_t do_catsep_more(substr /*buf*/, Sep const& /*sep*/, std::tuple<Types...> const& /*tp*/) { return 0; } + template< class Sep > static size_t do_uncatsep_more(csubstr /*buf*/, Sep & /*sep*/, std::tuple<Types...> & /*tp*/) { return 0; } + + static size_t do_format(substr buf, csubstr fmt, std::tuple<Types...> const& /*tp*/) + { + return to_chars(buf, fmt); + } + + static size_t do_unformat(csubstr buf, csubstr fmt, std::tuple<Types...> const& /*tp*/) + { + return 0; + } +}; + +} // namespace detail + +template< class... Types > +inline size_t cat(substr buf, std::tuple< Types... > const& tp) +{ + return detail::tuple_helper< 0, Types... >::do_cat(buf, tp); +} + +template< class... Types > +inline size_t uncat(csubstr buf, std::tuple< Types... > & tp) +{ + return detail::tuple_helper< 0, Types... >::do_uncat(buf, tp); +} + +template< class Sep, class... Types > +inline size_t catsep(substr buf, Sep const& sep, std::tuple< Types... > const& tp) +{ + size_t num = to_chars(buf, std::cref(std::get<0>(tp))); + buf = buf.len >= num ? buf.sub(num) : substr{}; + num += detail::tuple_helper< 1, Types... >::do_catsep_more(buf, sep, tp); + return num; +} + +template< class Sep, class... Types > +inline size_t uncatsep(csubstr buf, Sep & sep, std::tuple< Types... > & tp) +{ + size_t ret = from_str_trim(buf, &std::get<0>(tp)), num = ret; + if(ret == csubstr::npos) return csubstr::npos; + buf = buf.len >= ret ? buf.sub(ret) : substr{}; + ret = detail::tuple_helper< 1, Types... >::do_uncatsep_more(buf, sep, tp); + if(ret == csubstr::npos) return csubstr::npos; + num += ret; + return num; +} + +template< class... Types > +inline size_t format(substr buf, csubstr fmt, std::tuple< Types... > const& tp) +{ + return detail::tuple_helper< 0, Types... >::do_format(buf, fmt, tp); +} + +template< class... Types > +inline size_t unformat(csubstr buf, csubstr fmt, std::tuple< Types... > & tp) +{ + return detail::tuple_helper< 0, Types... >::do_unformat(buf, fmt, tp); +} +#endif // C4_TUPLE_TO_CHARS + +} // namespace c4 + +#endif /* _C4_STD_TUPLE_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/std/vector.hpp b/thirdparty/ryml/ext/c4core/src/c4/std/vector.hpp new file mode 100644 index 000000000..baaa24223 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/std/vector.hpp @@ -0,0 +1,88 @@ +#ifndef _C4_STD_VECTOR_HPP_ +#define _C4_STD_VECTOR_HPP_ + +/** @file vector.hpp provides conversion and comparison facilities + * from/between std::vector<char> to c4::substr and c4::csubstr. + * @todo add to_span() and friends + */ + +#ifndef C4CORE_SINGLE_HEADER +#include "c4/substr.hpp" +#endif + +#include <vector> + +namespace c4 { + +//----------------------------------------------------------------------------- + +/** get a substr (writeable string view) of an existing std::vector<char> */ +template<class Alloc> +c4::substr to_substr(std::vector<char, Alloc> &vec) +{ + char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer. + return c4::substr(data, vec.size()); +} + +/** get a csubstr (read-only string) view of an existing std::vector<char> */ +template<class Alloc> +c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec) +{ + const char *data = vec.empty() ? nullptr : vec.data(); // data() may or may not return a null pointer. + return c4::csubstr(data, vec.size()); +} + +//----------------------------------------------------------------------------- +// comparisons between substrings and std::vector<char> + +template<class Alloc> C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss != to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss == to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss >= to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss > to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss <= to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s) { return ss < to_csubstr(s); } + +template<class Alloc> C4_ALWAYS_INLINE bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss != to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss == to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss <= to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss < to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss >= to_csubstr(s); } +template<class Alloc> C4_ALWAYS_INLINE bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss) { return ss > to_csubstr(s); } + +//----------------------------------------------------------------------------- + +/** copy a std::vector<char> to a writeable string view */ +template<class Alloc> +inline size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s) +{ + C4_ASSERT(!buf.overlaps(to_csubstr(s))); + size_t len = buf.len < s.size() ? buf.len : s.size(); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(len > 0) + { + memcpy(buf.str, s.data(), len); + } + return s.size(); // return the number of needed chars +} + +/** copy a string view to an existing std::vector<char> */ +template<class Alloc> +inline bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s) +{ + s->resize(buf.len); + C4_ASSERT(!buf.overlaps(to_csubstr(*s))); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(buf.len > 0) + { + memcpy(&(*s)[0], buf.str, buf.len); + } + return true; +} + +} // namespace c4 + +#endif // _C4_STD_VECTOR_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/std/vector_fwd.hpp b/thirdparty/ryml/ext/c4core/src/c4/std/vector_fwd.hpp new file mode 100644 index 000000000..a739b7d28 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/std/vector_fwd.hpp @@ -0,0 +1,60 @@ +#ifndef _C4_STD_VECTOR_FWD_HPP_ +#define _C4_STD_VECTOR_FWD_HPP_ + +/** @file vector_fwd.hpp */ + +#include <cstddef> + +// forward declarations for std::vector +#if defined(__GLIBCXX__) || defined(__GLIBCPP__) || defined(_MSC_VER) +#if defined(_MSC_VER) +__pragma(warning(push)) +__pragma(warning(disable : 4643)) +#endif +namespace std { +template<typename> class allocator; +template<typename T, typename Alloc> class vector; +} // namespace std +#if defined(_MSC_VER) +__pragma(warning(pop)) +#endif +#elif defined(_LIBCPP_ABI_NAMESPACE) +namespace std { +inline namespace _LIBCPP_ABI_NAMESPACE { +template<typename> class allocator; +template<typename T, typename Alloc> class vector; +} // namespace _LIBCPP_ABI_NAMESPACE +} // namespace std +#else +#error "unknown standard library" +#endif + +#ifndef C4CORE_SINGLE_HEADER +#include "c4/substr_fwd.hpp" +#endif + +namespace c4 { + +template<class Alloc> c4::substr to_substr(std::vector<char, Alloc> &vec); +template<class Alloc> c4::csubstr to_csubstr(std::vector<char, Alloc> const& vec); + +template<class Alloc> bool operator!= (c4::csubstr ss, std::vector<char, Alloc> const& s); +template<class Alloc> bool operator== (c4::csubstr ss, std::vector<char, Alloc> const& s); +template<class Alloc> bool operator>= (c4::csubstr ss, std::vector<char, Alloc> const& s); +template<class Alloc> bool operator> (c4::csubstr ss, std::vector<char, Alloc> const& s); +template<class Alloc> bool operator<= (c4::csubstr ss, std::vector<char, Alloc> const& s); +template<class Alloc> bool operator< (c4::csubstr ss, std::vector<char, Alloc> const& s); + +template<class Alloc> bool operator!= (std::vector<char, Alloc> const& s, c4::csubstr ss); +template<class Alloc> bool operator== (std::vector<char, Alloc> const& s, c4::csubstr ss); +template<class Alloc> bool operator>= (std::vector<char, Alloc> const& s, c4::csubstr ss); +template<class Alloc> bool operator> (std::vector<char, Alloc> const& s, c4::csubstr ss); +template<class Alloc> bool operator<= (std::vector<char, Alloc> const& s, c4::csubstr ss); +template<class Alloc> bool operator< (std::vector<char, Alloc> const& s, c4::csubstr ss); + +template<class Alloc> size_t to_chars(c4::substr buf, std::vector<char, Alloc> const& s); +template<class Alloc> bool from_chars(c4::csubstr buf, std::vector<char, Alloc> * s); + +} // namespace c4 + +#endif // _C4_STD_VECTOR_FWD_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/substr.hpp b/thirdparty/ryml/ext/c4core/src/c4/substr.hpp new file mode 100644 index 000000000..ead46727e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/substr.hpp @@ -0,0 +1,2246 @@ +#ifndef _C4_SUBSTR_HPP_ +#define _C4_SUBSTR_HPP_ + +/** @file substr.hpp read+write string views */ + +#include <string.h> +#include <ctype.h> +#include <type_traits> + +#include "c4/config.hpp" +#include "c4/error.hpp" +#include "c4/substr_fwd.hpp" + +#ifdef __clang__ +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wtype-limits" // disable warnings on size_t>=0, used heavily in assertions below. These assertions are a preparation step for providing the index type as a template parameter. +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif + + +namespace c4 { + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +namespace detail { + +template<typename C> +static inline void _do_reverse(C *C4_RESTRICT first, C *C4_RESTRICT last) +{ + while(last > first) + { + C tmp = *last; + *last-- = *first; + *first++ = tmp; + } +} + +} // namespace detail + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +// utility macros to deuglify SFINAE code; undefined after the class. +// https://stackoverflow.com/questions/43051882/how-to-disable-a-class-member-funrtion-for-certain-template-types +#define C4_REQUIRE_RW(ret_type) \ + template <typename U=C> \ + typename std::enable_if< ! std::is_const<U>::value, ret_type>::type +// non-const-to-const +#define C4_NC2C(ty) \ + typename std::enable_if<std::is_const<C>::value && ( ! std::is_const<ty>::value), ty>::type + + +/** a non-owning string-view, consisting of a character pointer + * and a length. + * + * @note The pointer is explicitly restricted. + * @note Because of a C++ limitation, there cannot coexist overloads for + * constructing from a char[N] and a char*; the latter will always be chosen + * by the compiler. To construct an object of this type, call to_substr() or + * to_csubstr(). For a more detailed explanation on why the overloads cannot + * coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html + * + * @see to_substr() + * @see to_csubstr() + */ +template<class C> +struct C4CORE_EXPORT basic_substring +{ +public: + + /** a restricted pointer to the first character of the substring */ + C * C4_RESTRICT str; + /** the length of the substring */ + size_t len; + +public: + + /** @name Types */ + /** @{ */ + + using CC = typename std::add_const<C>::type; //!< CC=const char + using NCC_ = typename std::remove_const<C>::type; //!< NCC_=non const char + + using ro_substr = basic_substring<CC>; + using rw_substr = basic_substring<NCC_>; + + using char_type = C; + using size_type = size_t; + + using iterator = C*; + using const_iterator = CC*; + + enum : size_t { npos = (size_t)-1, NONE = (size_t)-1 }; + + /// convert automatically to substring of const C + operator ro_substr () const { ro_substr s(str, len); return s; } + + /** @} */ + +public: + + /** @name Default construction and assignment */ + /** @{ */ + + constexpr basic_substring() : str(nullptr), len(0) {} + + constexpr basic_substring(basic_substring const&) = default; + constexpr basic_substring(basic_substring &&) = default; + constexpr basic_substring(std::nullptr_t) : str(nullptr), len(0) {} + + basic_substring& operator= (basic_substring const&) = default; + basic_substring& operator= (basic_substring &&) = default; + basic_substring& operator= (std::nullptr_t) { str = nullptr; len = 0; return *this; } + + /** @} */ + +public: + + /** @name Construction and assignment from characters with the same type */ + /** @{ */ + + //basic_substring(C *s_) : str(s_), len(s_ ? strlen(s_) : 0) {} + /** the overload for receiving a single C* pointer will always + * hide the array[N] overload. So it is disabled. If you want to + * construct a substr from a single pointer containing a C-style string, + * you can call c4::to_substr()/c4::to_csubstr(). + * @see c4::to_substr() + * @see c4::to_csubstr() */ + template<size_t N> + constexpr basic_substring(C (&s_)[N]) noexcept : str(s_), len(N-1) {} + basic_substring(C *s_, size_t len_) : str(s_), len(len_) { C4_ASSERT(str || !len_); } + basic_substring(C *beg_, C *end_) : str(beg_), len(static_cast<size_t>(end_ - beg_)) { C4_ASSERT(end_ >= beg_); } + + //basic_substring& operator= (C *s_) { this->assign(s_); return *this; } + template<size_t N> + basic_substring& operator= (C (&s_)[N]) { this->assign<N>(s_); return *this; } + + //void assign(C *s_) { str = (s_); len = (s_ ? strlen(s_) : 0); } + /** the overload for receiving a single C* pointer will always + * hide the array[N] overload. So it is disabled. If you want to + * construct a substr from a single pointer containing a C-style string, + * you can call c4::to_substr()/c4::to_csubstr(). + * @see c4::to_substr() + * @see c4::to_csubstr() */ + template<size_t N> + void assign(C (&s_)[N]) { str = (s_); len = (N-1); } + void assign(C *s_, size_t len_) { str = s_; len = len_; C4_ASSERT(str || !len_); } + void assign(C *beg_, C *end_) { C4_ASSERT(end_ >= beg_); str = (beg_); len = (end_ - beg_); } + + void clear() { str = nullptr; len = 0; } + + /** @} */ + +public: + + /** @name Construction from non-const characters */ + /** @{ */ + + // when the char type is const, allow construction and assignment from non-const chars + + /** only available when the char type is const */ + template<size_t N, class U=NCC_> explicit basic_substring(C4_NC2C(U) (&s_)[N]) { str = s_; len = N-1; } + /** only available when the char type is const */ + template< class U=NCC_> basic_substring(C4_NC2C(U) *s_, size_t len_) { str = s_; len = len_; } + /** only available when the char type is const */ + template< class U=NCC_> basic_substring(C4_NC2C(U) *beg_, C4_NC2C(U) *end_) { C4_ASSERT(end_ >= beg_); str = beg_; len = end_ - beg_; } + + /** only available when the char type is const */ + template<size_t N, class U=NCC_> void assign(C4_NC2C(U) (&s_)[N]) { str = s_; len = N-1; } + /** only available when the char type is const */ + template< class U=NCC_> void assign(C4_NC2C(U) *s_, size_t len_) { str = s_; len = len_; } + /** only available when the char type is const */ + template< class U=NCC_> void assign(C4_NC2C(U) *beg_, C4_NC2C(U) *end_) { C4_ASSERT(end_ >= beg_); str = beg_; len = end_ - beg_; } + + /** only available when the char type is const */ + template<size_t N, class U=NCC_> + basic_substring& operator=(C4_NC2C(U) (&s_)[N]) { str = s_; len = N-1; return *this; } + + /** @} */ + +public: + + /** @name Standard accessor methods */ + /** @{ */ + + C4_ALWAYS_INLINE C4_PURE bool has_str() const noexcept { return ! empty() && str[0] != C(0); } + C4_ALWAYS_INLINE C4_PURE bool empty() const noexcept { return (len == 0 || str == nullptr); } + C4_ALWAYS_INLINE C4_PURE bool not_empty() const noexcept { return (len != 0 && str != nullptr); } + C4_ALWAYS_INLINE C4_PURE size_t size() const noexcept { return len; } + + C4_ALWAYS_INLINE C4_PURE iterator begin() noexcept { return str; } + C4_ALWAYS_INLINE C4_PURE iterator end () noexcept { return str + len; } + + C4_ALWAYS_INLINE C4_PURE const_iterator begin() const noexcept { return str; } + C4_ALWAYS_INLINE C4_PURE const_iterator end () const noexcept { return str + len; } + + C4_ALWAYS_INLINE C4_PURE C * data() noexcept { return str; } + C4_ALWAYS_INLINE C4_PURE C const* data() const noexcept { return str; } + + C4_ALWAYS_INLINE C4_PURE C & operator[] (size_t i) noexcept { C4_ASSERT(i >= 0 && i < len); return str[i]; } + C4_ALWAYS_INLINE C4_PURE C const& operator[] (size_t i) const noexcept { C4_ASSERT(i >= 0 && i < len); return str[i]; } + + C4_ALWAYS_INLINE C4_PURE C & front() noexcept { C4_ASSERT(len > 0 && str != nullptr); return *str; } + C4_ALWAYS_INLINE C4_PURE C const& front() const noexcept { C4_ASSERT(len > 0 && str != nullptr); return *str; } + + C4_ALWAYS_INLINE C4_PURE C & back() noexcept { C4_ASSERT(len > 0 && str != nullptr); return *(str + len - 1); } + C4_ALWAYS_INLINE C4_PURE C const& back() const noexcept { C4_ASSERT(len > 0 && str != nullptr); return *(str + len - 1); } + + /** @} */ + +public: + + /** @name Comparison methods */ + /** @{ */ + + C4_PURE int compare(C const c) const noexcept + { + C4_XASSERT((str != nullptr) || len == 0); + if(C4_LIKELY(str != nullptr && len > 0)) + return (*str != c) ? *str - c : (static_cast<int>(len) - 1); + else + return -1; + } + + C4_PURE int compare(const char *C4_RESTRICT that, size_t sz) const noexcept + { + C4_XASSERT(that || sz == 0); + C4_XASSERT(str || len == 0); + if(C4_LIKELY(str && that)) + { + { + const size_t min = len < sz ? len : sz; + for(size_t i = 0; i < min; ++i) + if(str[i] != that[i]) + return str[i] < that[i] ? -1 : 1; + } + if(len < sz) + return -1; + else if(len == sz) + return 0; + else + return 1; + } + else if(len == sz) + { + C4_XASSERT(len == 0 && sz == 0); + return 0; + } + return len < sz ? -1 : 1; + } + + C4_ALWAYS_INLINE C4_PURE int compare(ro_substr const that) const noexcept { return this->compare(that.str, that.len); } + + C4_ALWAYS_INLINE C4_PURE bool operator== (std::nullptr_t) const noexcept { return str == nullptr; } + C4_ALWAYS_INLINE C4_PURE bool operator!= (std::nullptr_t) const noexcept { return str != nullptr; } + + C4_ALWAYS_INLINE C4_PURE bool operator== (C const c) const noexcept { return this->compare(c) == 0; } + C4_ALWAYS_INLINE C4_PURE bool operator!= (C const c) const noexcept { return this->compare(c) != 0; } + C4_ALWAYS_INLINE C4_PURE bool operator< (C const c) const noexcept { return this->compare(c) < 0; } + C4_ALWAYS_INLINE C4_PURE bool operator> (C const c) const noexcept { return this->compare(c) > 0; } + C4_ALWAYS_INLINE C4_PURE bool operator<= (C const c) const noexcept { return this->compare(c) <= 0; } + C4_ALWAYS_INLINE C4_PURE bool operator>= (C const c) const noexcept { return this->compare(c) >= 0; } + + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator== (basic_substring<U> const that) const noexcept { return this->compare(that) == 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator!= (basic_substring<U> const that) const noexcept { return this->compare(that) != 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator< (basic_substring<U> const that) const noexcept { return this->compare(that) < 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator> (basic_substring<U> const that) const noexcept { return this->compare(that) > 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator<= (basic_substring<U> const that) const noexcept { return this->compare(that) <= 0; } + template<class U> C4_ALWAYS_INLINE C4_PURE bool operator>= (basic_substring<U> const that) const noexcept { return this->compare(that) >= 0; } + + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator== (const char (&that)[N]) const noexcept { return this->compare(that, N-1) == 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator!= (const char (&that)[N]) const noexcept { return this->compare(that, N-1) != 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator< (const char (&that)[N]) const noexcept { return this->compare(that, N-1) < 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator> (const char (&that)[N]) const noexcept { return this->compare(that, N-1) > 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator<= (const char (&that)[N]) const noexcept { return this->compare(that, N-1) <= 0; } + template<size_t N> C4_ALWAYS_INLINE C4_PURE bool operator>= (const char (&that)[N]) const noexcept { return this->compare(that, N-1) >= 0; } + + /** @} */ + +public: + + /** @name Sub-selection methods */ + /** @{ */ + + /** true if *this is a substring of that (ie, from the same buffer) */ + C4_ALWAYS_INLINE C4_PURE bool is_sub(ro_substr const that) const noexcept + { + return that.is_super(*this); + } + + /** true if that is a substring of *this (ie, from the same buffer) */ + C4_ALWAYS_INLINE C4_PURE bool is_super(ro_substr const that) const noexcept + { + if(C4_LIKELY(len > 0)) + return that.str >= str && that.str+that.len <= str+len; + else + return that.len == 0 && that.str == str && str != nullptr; + } + + /** true if there is overlap of at least one element between that and *this */ + C4_ALWAYS_INLINE C4_PURE bool overlaps(ro_substr const that) const noexcept + { + // thanks @timwynants + return that.str+that.len > str && that.str < str+len; + } + +public: + + /** return [first,len[ */ + C4_ALWAYS_INLINE C4_PURE basic_substring sub(size_t first) const noexcept + { + C4_ASSERT(first >= 0 && first <= len); + return basic_substring(str + first, len - first); + } + + /** return [first,first+num[. If num==npos, return [first,len[ */ + C4_ALWAYS_INLINE C4_PURE basic_substring sub(size_t first, size_t num) const noexcept + { + C4_ASSERT(first >= 0 && first <= len); + C4_ASSERT((num >= 0 && num <= len) || (num == npos)); + size_t rnum = num != npos ? num : len - first; + C4_ASSERT((first >= 0 && first + rnum <= len) || (num == 0)); + return basic_substring(str + first, rnum); + } + + /** return [first,last[. If last==npos, return [first,len[ */ + C4_ALWAYS_INLINE C4_PURE basic_substring range(size_t first, size_t last=npos) const noexcept + { + C4_ASSERT(first >= 0 && first <= len); + last = last != npos ? last : len; + C4_ASSERT(first <= last); + C4_ASSERT(last >= 0 && last <= len); + return basic_substring(str + first, last - first); + } + + /** return the first @p num elements: [0,num[*/ + C4_ALWAYS_INLINE C4_PURE basic_substring first(size_t num) const noexcept + { + C4_ASSERT(num <= len || num == npos); + return basic_substring(str, num != npos ? num : len); + } + + /** return the last @num elements: [len-num,len[*/ + C4_ALWAYS_INLINE C4_PURE basic_substring last(size_t num) const noexcept + { + C4_ASSERT(num <= len || num == npos); + return num != npos ? + basic_substring(str + len - num, num) : + *this; + } + + /** offset from the ends: return [left,len-right[ ; ie, trim a + number of characters from the left and right. This is + equivalent to python's negative list indices. */ + C4_ALWAYS_INLINE C4_PURE basic_substring offs(size_t left, size_t right) const noexcept + { + C4_ASSERT(left >= 0 && left <= len); + C4_ASSERT(right >= 0 && right <= len); + C4_ASSERT(left <= len - right + 1); + return basic_substring(str + left, len - right - left); + } + + /** return [0, pos[ . Same as .first(pos), but provided for compatibility with .right_of() */ + C4_ALWAYS_INLINE C4_PURE basic_substring left_of(size_t pos) const noexcept + { + C4_ASSERT(pos <= len || pos == npos); + return (pos != npos) ? + basic_substring(str, pos) : + *this; + } + + /** return [0, pos+include_pos[ . Same as .first(pos+1), but provided for compatibility with .right_of() */ + C4_ALWAYS_INLINE C4_PURE basic_substring left_of(size_t pos, bool include_pos) const noexcept + { + C4_ASSERT(pos <= len || pos == npos); + return (pos != npos) ? + basic_substring(str, pos+include_pos) : + *this; + } + + /** return [pos+1, len[ */ + C4_ALWAYS_INLINE C4_PURE basic_substring right_of(size_t pos) const noexcept + { + C4_ASSERT(pos <= len || pos == npos); + return (pos != npos) ? + basic_substring(str + (pos + 1), len - (pos + 1)) : + basic_substring(str + len, size_t(0)); + } + + /** return [pos+!include_pos, len[ */ + C4_ALWAYS_INLINE C4_PURE basic_substring right_of(size_t pos, bool include_pos) const noexcept + { + C4_ASSERT(pos <= len || pos == npos); + return (pos != npos) ? + basic_substring(str + (pos + !include_pos), len - (pos + !include_pos)) : + basic_substring(str + len, size_t(0)); + } + +public: + + /** given @p subs a substring of the current string, get the + * portion of the current string to the left of it */ + C4_ALWAYS_INLINE C4_PURE basic_substring left_of(ro_substr const subs) const noexcept + { + C4_ASSERT(is_super(subs) || subs.empty()); + auto ssb = subs.begin(); + auto b = begin(); + auto e = end(); + if(ssb >= b && ssb <= e) + return sub(0, static_cast<size_t>(ssb - b)); + else + return sub(0, 0); + } + + /** given @p subs a substring of the current string, get the + * portion of the current string to the right of it */ + C4_ALWAYS_INLINE C4_PURE basic_substring right_of(ro_substr const subs) const noexcept + { + C4_ASSERT(is_super(subs) || subs.empty()); + auto sse = subs.end(); + auto b = begin(); + auto e = end(); + if(sse >= b && sse <= e) + return sub(static_cast<size_t>(sse - b), static_cast<size_t>(e - sse)); + else + return sub(0, 0); + } + + /** @} */ + +public: + + /** @name Removing characters (trim()) / patterns (strip()) from the tips of the string */ + /** @{ */ + + /** trim left */ + basic_substring triml(const C c) const + { + if( ! empty()) + { + size_t pos = first_not_of(c); + if(pos != npos) + return sub(pos); + } + return sub(0, 0); + } + /** trim left ANY of the characters. + * @see stripl() to remove a pattern from the left */ + basic_substring triml(ro_substr chars) const + { + if( ! empty()) + { + size_t pos = first_not_of(chars); + if(pos != npos) + return sub(pos); + } + return sub(0, 0); + } + + /** trim the character c from the right */ + basic_substring trimr(const C c) const + { + if( ! empty()) + { + size_t pos = last_not_of(c, npos); + if(pos != npos) + return sub(0, pos+1); + } + return sub(0, 0); + } + /** trim right ANY of the characters + * @see stripr() to remove a pattern from the right */ + basic_substring trimr(ro_substr chars) const + { + if( ! empty()) + { + size_t pos = last_not_of(chars, npos); + if(pos != npos) + return sub(0, pos+1); + } + return sub(0, 0); + } + + /** trim the character c left and right */ + basic_substring trim(const C c) const + { + return triml(c).trimr(c); + } + /** trim left and right ANY of the characters + * @see strip() to remove a pattern from the left and right */ + basic_substring trim(ro_substr const chars) const + { + return triml(chars).trimr(chars); + } + + /** remove a pattern from the left + * @see triml() to remove characters*/ + basic_substring stripl(ro_substr pattern) const + { + if( ! begins_with(pattern)) + return *this; + return sub(pattern.len < len ? pattern.len : len); + } + + /** remove a pattern from the right + * @see trimr() to remove characters*/ + basic_substring stripr(ro_substr pattern) const + { + if( ! ends_with(pattern)) + return *this; + return left_of(len - (pattern.len < len ? pattern.len : len)); + } + + /** @} */ + +public: + + /** @name Lookup methods */ + /** @{ */ + + inline size_t find(const C c, size_t start_pos=0) const + { + return first_of(c, start_pos); + } + inline size_t find(ro_substr pattern, size_t start_pos=0) const + { + C4_ASSERT(start_pos == npos || (start_pos >= 0 && start_pos <= len)); + if(len < pattern.len) return npos; + for(size_t i = start_pos, e = len - pattern.len + 1; i < e; ++i) + { + bool gotit = true; + for(size_t j = 0; j < pattern.len; ++j) + { + C4_ASSERT(i + j < len); + if(str[i + j] != pattern.str[j]) + { + gotit = false; + break; + } + } + if(gotit) + { + return i; + } + } + return npos; + } + +public: + + /** count the number of occurrences of c */ + inline size_t count(const C c, size_t pos=0) const + { + C4_ASSERT(pos >= 0 && pos <= len); + size_t num = 0; + pos = find(c, pos); + while(pos != npos) + { + ++num; + pos = find(c, pos + 1); + } + return num; + } + + /** count the number of occurrences of s */ + inline size_t count(ro_substr c, size_t pos=0) const + { + C4_ASSERT(pos >= 0 && pos <= len); + size_t num = 0; + pos = find(c, pos); + while(pos != npos) + { + ++num; + pos = find(c, pos + c.len); + } + return num; + } + + /** get the substr consisting of the first occurrence of @p c after @p pos, or an empty substr if none occurs */ + inline basic_substring select(const C c, size_t pos=0) const + { + pos = find(c, pos); + return pos != npos ? sub(pos, 1) : basic_substring(); + } + + /** get the substr consisting of the first occurrence of @p pattern after @p pos, or an empty substr if none occurs */ + inline basic_substring select(ro_substr pattern, size_t pos=0) const + { + pos = find(pattern, pos); + return pos != npos ? sub(pos, pattern.len) : basic_substring(); + } + +public: + + struct first_of_any_result + { + size_t which; + size_t pos; + inline operator bool() const { return which != NONE && pos != npos; } + }; + + first_of_any_result first_of_any(ro_substr s0, ro_substr s1) const + { + ro_substr s[2] = {s0, s1}; + return first_of_any_iter(&s[0], &s[0] + 2); + } + + first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2) const + { + ro_substr s[3] = {s0, s1, s2}; + return first_of_any_iter(&s[0], &s[0] + 3); + } + + first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2, ro_substr s3) const + { + ro_substr s[4] = {s0, s1, s2, s3}; + return first_of_any_iter(&s[0], &s[0] + 4); + } + + first_of_any_result first_of_any(ro_substr s0, ro_substr s1, ro_substr s2, ro_substr s3, ro_substr s4) const + { + ro_substr s[5] = {s0, s1, s2, s3, s4}; + return first_of_any_iter(&s[0], &s[0] + 5); + } + + template<class It> + first_of_any_result first_of_any_iter(It first_span, It last_span) const + { + for(size_t i = 0; i < len; ++i) + { + size_t curr = 0; + for(It it = first_span; it != last_span; ++curr, ++it) + { + auto const& chars = *it; + if((i + chars.len) > len) continue; + bool gotit = true; + for(size_t j = 0; j < chars.len; ++j) + { + C4_ASSERT(i + j < len); + if(str[i + j] != chars[j]) + { + gotit = false; + break; + } + } + if(gotit) + { + return {curr, i}; + } + } + } + return {NONE, npos}; + } + +public: + + /** true if the first character of the string is @p c */ + bool begins_with(const C c) const + { + return len > 0 ? str[0] == c : false; + } + + /** true if the first @p num characters of the string are @p c */ + bool begins_with(const C c, size_t num) const + { + if(len < num) + { + return false; + } + for(size_t i = 0; i < num; ++i) + { + if(str[i] != c) + { + return false; + } + } + return true; + } + + /** true if the string begins with the given @p pattern */ + bool begins_with(ro_substr pattern) const + { + if(len < pattern.len) + { + return false; + } + for(size_t i = 0; i < pattern.len; ++i) + { + if(str[i] != pattern[i]) + { + return false; + } + } + return true; + } + + /** true if the first character of the string is any of the given @p chars */ + bool begins_with_any(ro_substr chars) const + { + if(len == 0) + { + return false; + } + for(size_t i = 0; i < chars.len; ++i) + { + if(str[0] == chars.str[i]) + { + return true; + } + } + return false; + } + + /** true if the last character of the string is @p c */ + bool ends_with(const C c) const + { + return len > 0 ? str[len-1] == c : false; + } + + /** true if the last @p num characters of the string are @p c */ + bool ends_with(const C c, size_t num) const + { + if(len < num) + { + return false; + } + for(size_t i = len - num; i < len; ++i) + { + if(str[i] != c) + { + return false; + } + } + return true; + } + + /** true if the string ends with the given @p pattern */ + bool ends_with(ro_substr pattern) const + { + if(len < pattern.len) + { + return false; + } + for(size_t i = 0, s = len-pattern.len; i < pattern.len; ++i) + { + if(str[s+i] != pattern[i]) + { + return false; + } + } + return true; + } + + /** true if the last character of the string is any of the given @p chars */ + bool ends_with_any(ro_substr chars) const + { + if(len == 0) + { + return false; + } + for(size_t i = 0; i < chars.len; ++i) + { + if(str[len - 1] == chars[i]) + { + return true; + } + } + return false; + } + +public: + + /** @return the first position where c is found in the string, or npos if none is found */ + size_t first_of(const C c, size_t start=0) const + { + C4_ASSERT(start == npos || (start >= 0 && start <= len)); + for(size_t i = start; i < len; ++i) + { + if(str[i] == c) + return i; + } + return npos; + } + + /** @return the last position where c is found in the string, or npos if none is found */ + size_t last_of(const C c, size_t start=npos) const + { + C4_ASSERT(start == npos || (start >= 0 && start <= len)); + if(start == npos) + start = len; + for(size_t i = start-1; i != size_t(-1); --i) + { + if(str[i] == c) + return i; + } + return npos; + } + + /** @return the first position where ANY of the chars is found in the string, or npos if none is found */ + size_t first_of(ro_substr chars, size_t start=0) const + { + C4_ASSERT(start == npos || (start >= 0 && start <= len)); + for(size_t i = start; i < len; ++i) + { + for(size_t j = 0; j < chars.len; ++j) + { + if(str[i] == chars[j]) + return i; + } + } + return npos; + } + + /** @return the last position where ANY of the chars is found in the string, or npos if none is found */ + size_t last_of(ro_substr chars, size_t start=npos) const + { + C4_ASSERT(start == npos || (start >= 0 && start <= len)); + if(start == npos) + start = len; + for(size_t i = start-1; i != size_t(-1); --i) + { + for(size_t j = 0; j < chars.len; ++j) + { + if(str[i] == chars[j]) + return i; + } + } + return npos; + } + +public: + + size_t first_not_of(const C c, size_t start=0) const + { + C4_ASSERT((start >= 0 && start <= len) || (start == len && len == 0)); + for(size_t i = start; i < len; ++i) + { + if(str[i] != c) + return i; + } + return npos; + } + + size_t last_not_of(const C c, size_t start=npos) const + { + C4_ASSERT(start == npos || (start >= 0 && start <= len)); + if(start == npos) + start = len; + for(size_t i = start-1; i != size_t(-1); --i) + { + if(str[i] != c) + return i; + } + return npos; + } + + size_t first_not_of(ro_substr chars, size_t start=0) const + { + C4_ASSERT((start >= 0 && start <= len) || (start == len && len == 0)); + for(size_t i = start; i < len; ++i) + { + bool gotit = true; + for(size_t j = 0; j < chars.len; ++j) + { + if(str[i] == chars.str[j]) + { + gotit = false; + break; + } + } + if(gotit) + { + return i; + } + } + return npos; + } + + size_t last_not_of(ro_substr chars, size_t start=npos) const + { + C4_ASSERT(start == npos || (start >= 0 && start <= len)); + if(start == npos) + start = len; + for(size_t i = start-1; i != size_t(-1); --i) + { + bool gotit = true; + for(size_t j = 0; j < chars.len; ++j) + { + if(str[i] == chars.str[j]) + { + gotit = false; + break; + } + } + if(gotit) + { + return i; + } + } + return npos; + } + + /** @} */ + +public: + + /** @name Range lookup methods */ + /** @{ */ + + /** get the range delimited by an open-close pair of characters. + * @note There must be no nested pairs. + * @note No checks for escapes are performed. */ + basic_substring pair_range(CC open, CC close) const + { + size_t b = find(open); + if(b == npos) + return basic_substring(); + size_t e = find(close, b+1); + if(e == npos) + return basic_substring(); + basic_substring ret = range(b, e+1); + C4_ASSERT(ret.sub(1).find(open) == npos); + return ret; + } + + /** get the range delimited by a single open-close character (eg, quotes). + * @note The open-close character can be escaped. */ + basic_substring pair_range_esc(CC open_close, CC escape=CC('\\')) + { + size_t b = find(open_close); + if(b == npos) return basic_substring(); + for(size_t i = b+1; i < len; ++i) + { + CC c = str[i]; + if(c == open_close) + { + if(str[i-1] != escape) + { + return range(b, i+1); + } + } + } + return basic_substring(); + } + + /** get the range delimited by an open-close pair of characters, + * with possibly nested occurrences. No checks for escapes are + * performed. */ + basic_substring pair_range_nested(CC open, CC close) const + { + size_t b = find(open); + if(b == npos) return basic_substring(); + size_t e, curr = b+1, count = 0; + const char both[] = {open, close, '\0'}; + while((e = first_of(both, curr)) != npos) + { + if(str[e] == open) + { + ++count; + curr = e+1; + } + else if(str[e] == close) + { + if(count == 0) return range(b, e+1); + --count; + curr = e+1; + } + } + return basic_substring(); + } + + basic_substring unquoted() const + { + constexpr const C dq('"'), sq('\''); + if(len >= 2 && (str[len - 2] != C('\\')) && + ((begins_with(sq) && ends_with(sq)) + || + (begins_with(dq) && ends_with(dq)))) + { + return range(1, len -1); + } + return *this; + } + + /** @} */ + +public: + + /** @name Number-matching query methods */ + /** @{ */ + + /** @return true if the substring contents are a floating-point or integer number. + * @note any leading or trailing whitespace will return false. */ + bool is_number() const + { + if(empty() || (first_non_empty_span().empty())) + return false; + if(first_uint_span() == *this) + return true; + if(first_int_span() == *this) + return true; + if(first_real_span() == *this) + return true; + return false; + } + + /** @return true if the substring contents are a real number. + * @note any leading or trailing whitespace will return false. */ + bool is_real() const + { + if(empty() || (first_non_empty_span().empty())) + return false; + if(first_real_span() == *this) + return true; + return false; + } + + /** @return true if the substring contents are an integer number. + * @note any leading or trailing whitespace will return false. */ + bool is_integer() const + { + if(empty() || (first_non_empty_span().empty())) + return false; + if(first_uint_span() == *this) + return true; + if(first_int_span() == *this) + return true; + return false; + } + + /** @return true if the substring contents are an unsigned integer number. + * @note any leading or trailing whitespace will return false. */ + bool is_unsigned_integer() const + { + if(empty() || (first_non_empty_span().empty())) + return false; + if(first_uint_span() == *this) + return true; + return false; + } + + /** get the first span consisting exclusively of non-empty characters */ + basic_substring first_non_empty_span() const + { + constexpr const ro_substr empty_chars(" \n\r\t"); + size_t pos = first_not_of(empty_chars); + if(pos == npos) + return first(0); + auto ret = sub(pos); + pos = ret.first_of(empty_chars); + return ret.first(pos); + } + + /** get the first span which can be interpreted as an unsigned integer */ + basic_substring first_uint_span() const + { + basic_substring ne = first_non_empty_span(); + if(ne.empty()) + return ne; + if(ne.str[0] == '-') + return first(0); + size_t skip_start = (ne.str[0] == '+') ? 1 : 0; + return ne._first_integral_span(skip_start); + } + + /** get the first span which can be interpreted as a signed integer */ + basic_substring first_int_span() const + { + basic_substring ne = first_non_empty_span(); + if(ne.empty()) + return ne; + size_t skip_start = (ne.str[0] == '+' || ne.str[0] == '-') ? 1 : 0; + return ne._first_integral_span(skip_start); + } + + basic_substring _first_integral_span(size_t skip_start) const + { + C4_ASSERT(!empty()); + if(skip_start == len) + return first(0); + C4_ASSERT(skip_start < len); + if(len >= skip_start + 3) + { + if(str[skip_start] != '0') + { + for(size_t i = skip_start; i < len; ++i) + { + char c = str[i]; + if(c < '0' || c > '9') + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); + } + } + else + { + char next = str[skip_start + 1]; + if(next == 'x' || next == 'X') + { + skip_start += 2; + for(size_t i = skip_start; i < len; ++i) + { + const char c = str[i]; + if( ! _is_hex_char(c)) + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); + } + return *this; + } + else if(next == 'b' || next == 'B') + { + skip_start += 2; + for(size_t i = skip_start; i < len; ++i) + { + const char c = str[i]; + if(c != '0' && c != '1') + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); + } + return *this; + } + else if(next == 'o' || next == 'O') + { + skip_start += 2; + for(size_t i = skip_start; i < len; ++i) + { + const char c = str[i]; + if(c < '0' || c > '7') + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); + } + return *this; + } + } + } + // must be a decimal, or it is not a an number + for(size_t i = skip_start; i < len; ++i) + { + const char c = str[i]; + if(c < '0' || c > '9') + return i > skip_start && _is_delim_char(c) ? first(i) : first(0); + } + return *this; + } + + /** get the first span which can be interpreted as a real (floating-point) number */ + basic_substring first_real_span() const + { + basic_substring ne = first_non_empty_span(); + if(ne.empty()) + return ne; + size_t skip_start = (ne.str[0] == '+' || ne.str[0] == '-'); + C4_ASSERT(skip_start == 0 || skip_start == 1); + // if we have at least three digits after the leading sign, it + // can be decimal, or hex, or bin or oct. Ex: + // non-decimal: 0x0, 0b0, 0o0 + // decimal: 1.0, 10., 1e1, 100, inf, nan, infinity + if(ne.len >= skip_start+3) + { + // if it does not have leading 0, it must be decimal, or it is not a real + if(ne.str[skip_start] != '0') + { + if(ne.str[skip_start] == 'i') // is it infinity or inf? + { + basic_substring word = ne._word_follows(skip_start + 1, "nfinity"); + if(word.len) + return word; + return ne._word_follows(skip_start + 1, "nf"); + } + else if(ne.str[skip_start] == 'n') // is it nan? + { + return ne._word_follows(skip_start + 1, "an"); + } + else // must be a decimal, or it is not a real + { + return ne._first_real_span_dec(skip_start); + } + } + else // starts with 0. is it 0x, 0b or 0o? + { + const char next = ne.str[skip_start + 1]; + // hexadecimal + if(next == 'x' || next == 'X') + return ne._first_real_span_hex(skip_start + 2); + // binary + else if(next == 'b' || next == 'B') + return ne._first_real_span_bin(skip_start + 2); + // octal + else if(next == 'o' || next == 'O') + return ne._first_real_span_oct(skip_start + 2); + // none of the above. may still be a decimal. + else + return ne._first_real_span_dec(skip_start); // do not skip the 0. + } + } + // less than 3 chars after the leading sign. It is either a + // decimal or it is not a real. (cannot be any of 0x0, etc). + return ne._first_real_span_dec(skip_start); + } + + /** true if the character is a delimiter character *at the end* */ + static constexpr C4_ALWAYS_INLINE C4_CONST bool _is_delim_char(char c) noexcept + { + return c == ' ' || c == '\n' + || c == ']' || c == ')' || c == '}' + || c == ',' || c == ';' || c == '\r' || c == '\t' || c == '\0'; + } + + /** true if the character is in [0-9a-fA-F] */ + static constexpr C4_ALWAYS_INLINE C4_CONST bool _is_hex_char(char c) noexcept + { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + } + + C4_NO_INLINE C4_PURE basic_substring _word_follows(size_t pos, csubstr word) const noexcept + { + size_t posend = pos + word.len; + if(len >= posend && sub(pos, word.len) == word) + if(len == posend || _is_delim_char(str[posend])) + return first(posend); + return first(0); + } + + // this function is declared inside the class to avoid a VS error with __declspec(dllimport) + C4_NO_INLINE C4_PURE basic_substring _first_real_span_dec(size_t pos) const noexcept + { + bool intchars = false; + bool fracchars = false; + bool powchars; + // integral part + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + { + intchars = true; + } + else if(c == '.') + { + ++pos; + goto fractional_part_dec; + } + else if(c == 'e' || c == 'E') + { + ++pos; + goto power_part_dec; + } + else if(_is_delim_char(c)) + { + return intchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + // no . or p were found; this is either an integral number + // or not a number at all + return intchars ? + *this : + first(0); + fractional_part_dec: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == '.'); + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + { + fracchars = true; + } + else if(c == 'e' || c == 'E') + { + ++pos; + goto power_part_dec; + } + else if(_is_delim_char(c)) + { + return intchars || fracchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + return intchars || fracchars ? + *this : + first(0); + power_part_dec: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == 'e' || str[pos - 1] == 'E'); + // either a + or a - is expected here, followed by more chars. + // also, using (pos+1) in this check will cause an early + // return when no more chars follow the sign. + if(len <= (pos+1) || ((!intchars) && (!fracchars))) + return first(0); + ++pos; // this was the sign. + // ... so the (pos+1) ensures that we enter the loop and + // hence that there exist chars in the power part + powchars = false; + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + powchars = true; + else if(powchars && _is_delim_char(c)) + return first(pos); + else + return first(0); + } + return *this; + } + + // this function is declared inside the class to avoid a VS error with __declspec(dllimport) + C4_NO_INLINE C4_PURE basic_substring _first_real_span_hex(size_t pos) const noexcept + { + bool intchars = false; + bool fracchars = false; + bool powchars; + // integral part + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(_is_hex_char(c)) + { + intchars = true; + } + else if(c == '.') + { + ++pos; + goto fractional_part_hex; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_hex; + } + else if(_is_delim_char(c)) + { + return intchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + // no . or p were found; this is either an integral number + // or not a number at all + return intchars ? + *this : + first(0); + fractional_part_hex: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == '.'); + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(_is_hex_char(c)) + { + fracchars = true; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_hex; + } + else if(_is_delim_char(c)) + { + return intchars || fracchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + return intchars || fracchars ? + *this : + first(0); + power_part_hex: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == 'p' || str[pos - 1] == 'P'); + // either a + or a - is expected here, followed by more chars. + // also, using (pos+1) in this check will cause an early + // return when no more chars follow the sign. + if(len <= (pos+1) || (str[pos] != '+' && str[pos] != '-') || ((!intchars) && (!fracchars))) + return first(0); + ++pos; // this was the sign. + // ... so the (pos+1) ensures that we enter the loop and + // hence that there exist chars in the power part + powchars = false; + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + powchars = true; + else if(powchars && _is_delim_char(c)) + return first(pos); + else + return first(0); + } + return *this; + } + + // this function is declared inside the class to avoid a VS error with __declspec(dllimport) + C4_NO_INLINE C4_PURE basic_substring _first_real_span_bin(size_t pos) const noexcept + { + bool intchars = false; + bool fracchars = false; + bool powchars; + // integral part + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c == '0' || c == '1') + { + intchars = true; + } + else if(c == '.') + { + ++pos; + goto fractional_part_bin; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_bin; + } + else if(_is_delim_char(c)) + { + return intchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + // no . or p were found; this is either an integral number + // or not a number at all + return intchars ? + *this : + first(0); + fractional_part_bin: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == '.'); + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c == '0' || c == '1') + { + fracchars = true; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_bin; + } + else if(_is_delim_char(c)) + { + return intchars || fracchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + return intchars || fracchars ? + *this : + first(0); + power_part_bin: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == 'p' || str[pos - 1] == 'P'); + // either a + or a - is expected here, followed by more chars. + // also, using (pos+1) in this check will cause an early + // return when no more chars follow the sign. + if(len <= (pos+1) || (str[pos] != '+' && str[pos] != '-') || ((!intchars) && (!fracchars))) + return first(0); + ++pos; // this was the sign. + // ... so the (pos+1) ensures that we enter the loop and + // hence that there exist chars in the power part + powchars = false; + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + powchars = true; + else if(powchars && _is_delim_char(c)) + return first(pos); + else + return first(0); + } + return *this; + } + + // this function is declared inside the class to avoid a VS error with __declspec(dllimport) + C4_NO_INLINE C4_PURE basic_substring _first_real_span_oct(size_t pos) const noexcept + { + bool intchars = false; + bool fracchars = false; + bool powchars; + // integral part + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '7') + { + intchars = true; + } + else if(c == '.') + { + ++pos; + goto fractional_part_oct; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_oct; + } + else if(_is_delim_char(c)) + { + return intchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + // no . or p were found; this is either an integral number + // or not a number at all + return intchars ? + *this : + first(0); + fractional_part_oct: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == '.'); + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '7') + { + fracchars = true; + } + else if(c == 'p' || c == 'P') + { + ++pos; + goto power_part_oct; + } + else if(_is_delim_char(c)) + { + return intchars || fracchars ? first(pos) : first(0); + } + else + { + return first(0); + } + } + return intchars || fracchars ? + *this : + first(0); + power_part_oct: + C4_ASSERT(pos > 0); + C4_ASSERT(str[pos - 1] == 'p' || str[pos - 1] == 'P'); + // either a + or a - is expected here, followed by more chars. + // also, using (pos+1) in this check will cause an early + // return when no more chars follow the sign. + if(len <= (pos+1) || (str[pos] != '+' && str[pos] != '-') || ((!intchars) && (!fracchars))) + return first(0); + ++pos; // this was the sign. + // ... so the (pos+1) ensures that we enter the loop and + // hence that there exist chars in the power part + powchars = false; + for( ; pos < len; ++pos) + { + const char c = str[pos]; + if(c >= '0' && c <= '9') + powchars = true; + else if(powchars && _is_delim_char(c)) + return first(pos); + else + return first(0); + } + return *this; + } + + /** @} */ + +public: + + /** @name Splitting methods */ + /** @{ */ + + /** returns true if the string has not been exhausted yet, meaning + * it's ok to call next_split() again. When no instance of sep + * exists in the string, returns the full string. When the input + * is an empty string, the output string is the empty string. */ + bool next_split(C sep, size_t *C4_RESTRICT start_pos, basic_substring *C4_RESTRICT out) const + { + if(C4_LIKELY(*start_pos < len)) + { + for(size_t i = *start_pos, e = len; i < e; i++) + { + if(str[i] == sep) + { + out->assign(str + *start_pos, i - *start_pos); + *start_pos = i+1; + return true; + } + } + out->assign(str + *start_pos, len - *start_pos); + *start_pos = len + 1; + return true; + } + else + { + bool valid = len > 0 && (*start_pos == len); + if(valid && !empty() && str[len-1] == sep) + { + out->assign(str + len, (size_t)0); // the cast is needed to prevent overload ambiguity + } + else + { + out->assign(str + len + 1, (size_t)0); // the cast is needed to prevent overload ambiguity + } + *start_pos = len + 1; + return valid; + } + } + +private: + + struct split_proxy_impl + { + struct split_iterator_impl + { + split_proxy_impl const* m_proxy; + basic_substring m_str; + size_t m_pos; + NCC_ m_sep; + + split_iterator_impl(split_proxy_impl const* proxy, size_t pos, C sep) + : m_proxy(proxy), m_pos(pos), m_sep(sep) + { + _tick(); + } + + void _tick() + { + m_proxy->m_str.next_split(m_sep, &m_pos, &m_str); + } + + split_iterator_impl& operator++ () { _tick(); return *this; } + split_iterator_impl operator++ (int) { split_iterator_impl it = *this; _tick(); return it; } + + basic_substring& operator* () { return m_str; } + basic_substring* operator-> () { return &m_str; } + + bool operator!= (split_iterator_impl const& that) const + { + return !(this->operator==(that)); + } + bool operator== (split_iterator_impl const& that) const + { + C4_XASSERT((m_sep == that.m_sep) && "cannot compare split iterators with different separators"); + if(m_str.size() != that.m_str.size()) + return false; + if(m_str.data() != that.m_str.data()) + return false; + return m_pos == that.m_pos; + } + }; + + basic_substring m_str; + size_t m_start_pos; + C m_sep; + + split_proxy_impl(basic_substring str_, size_t start_pos, C sep) + : m_str(str_), m_start_pos(start_pos), m_sep(sep) + { + } + + split_iterator_impl begin() const + { + auto it = split_iterator_impl(this, m_start_pos, m_sep); + return it; + } + split_iterator_impl end() const + { + size_t pos = m_str.size() + 1; + auto it = split_iterator_impl(this, pos, m_sep); + return it; + } + }; + +public: + + using split_proxy = split_proxy_impl; + + /** a view into the splits */ + split_proxy split(C sep, size_t start_pos=0) const + { + C4_XASSERT((start_pos >= 0 && start_pos < len) || empty()); + auto ss = sub(0, len); + auto it = split_proxy(ss, start_pos, sep); + return it; + } + +public: + + /** pop right: return the first split from the right. Use + * gpop_left() to get the reciprocal part. + */ + basic_substring pop_right(C sep=C('/'), bool skip_empty=false) const + { + if(C4_LIKELY(len > 1)) + { + auto pos = last_of(sep); + if(pos != npos) + { + if(pos + 1 < len) // does not end with sep + { + return sub(pos + 1); // return from sep to end + } + else // the string ends with sep + { + if( ! skip_empty) + { + return sub(pos + 1, 0); + } + auto ppos = last_not_of(sep); // skip repeated seps + if(ppos == npos) // the string is all made of seps + { + return sub(0, 0); + } + // find the previous sep + auto pos0 = last_of(sep, ppos); + if(pos0 == npos) // only the last sep exists + { + return sub(0); // return the full string (because skip_empty is true) + } + ++pos0; + return sub(pos0); + } + } + else // no sep was found, return the full string + { + return *this; + } + } + else if(len == 1) + { + if(begins_with(sep)) + { + return sub(0, 0); + } + return *this; + } + else // an empty string + { + return basic_substring(); + } + } + + /** return the first split from the left. Use gpop_right() to get + * the reciprocal part. */ + basic_substring pop_left(C sep = C('/'), bool skip_empty=false) const + { + if(C4_LIKELY(len > 1)) + { + auto pos = first_of(sep); + if(pos != npos) + { + if(pos > 0) // does not start with sep + { + return sub(0, pos); // return everything up to it + } + else // the string starts with sep + { + if( ! skip_empty) + { + return sub(0, 0); + } + auto ppos = first_not_of(sep); // skip repeated seps + if(ppos == npos) // the string is all made of seps + { + return sub(0, 0); + } + // find the next sep + auto pos0 = first_of(sep, ppos); + if(pos0 == npos) // only the first sep exists + { + return sub(0); // return the full string (because skip_empty is true) + } + C4_XASSERT(pos0 > 0); + // return everything up to the second sep + return sub(0, pos0); + } + } + else // no sep was found, return the full string + { + return sub(0); + } + } + else if(len == 1) + { + if(begins_with(sep)) + { + return sub(0, 0); + } + return sub(0); + } + else // an empty string + { + return basic_substring(); + } + } + +public: + + /** greedy pop left. eg, csubstr("a/b/c").gpop_left('/')="c" */ + basic_substring gpop_left(C sep = C('/'), bool skip_empty=false) const + { + auto ss = pop_right(sep, skip_empty); + ss = left_of(ss); + if(ss.find(sep) != npos) + { + if(ss.ends_with(sep)) + { + if(skip_empty) + { + ss = ss.trimr(sep); + } + else + { + ss = ss.sub(0, ss.len-1); // safe to subtract because ends_with(sep) is true + } + } + } + return ss; + } + + /** greedy pop right. eg, csubstr("a/b/c").gpop_right('/')="a" */ + basic_substring gpop_right(C sep = C('/'), bool skip_empty=false) const + { + auto ss = pop_left(sep, skip_empty); + ss = right_of(ss); + if(ss.find(sep) != npos) + { + if(ss.begins_with(sep)) + { + if(skip_empty) + { + ss = ss.triml(sep); + } + else + { + ss = ss.sub(1); + } + } + } + return ss; + } + + /** @} */ + +public: + + /** @name Path-like manipulation methods */ + /** @{ */ + + basic_substring basename(C sep=C('/')) const + { + auto ss = pop_right(sep, /*skip_empty*/true); + ss = ss.trimr(sep); + return ss; + } + + basic_substring dirname(C sep=C('/')) const + { + auto ss = basename(sep); + ss = ss.empty() ? *this : left_of(ss); + return ss; + } + + C4_ALWAYS_INLINE basic_substring name_wo_extshort() const + { + return gpop_left('.'); + } + + C4_ALWAYS_INLINE basic_substring name_wo_extlong() const + { + return pop_left('.'); + } + + C4_ALWAYS_INLINE basic_substring extshort() const + { + return pop_right('.'); + } + + C4_ALWAYS_INLINE basic_substring extlong() const + { + return gpop_right('.'); + } + + /** @} */ + +public: + + /** @name Content-modification methods (only for non-const C) */ + /** @{ */ + + /** convert the string to upper-case + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(void) toupper() + { + for(size_t i = 0; i < len; ++i) + { + str[i] = static_cast<C>(::toupper(str[i])); + } + } + + /** convert the string to lower-case + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(void) tolower() + { + for(size_t i = 0; i < len; ++i) + { + str[i] = static_cast<C>(::tolower(str[i])); + } + } + +public: + + /** fill the entire contents with the given @p val + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(void) fill(C val) + { + for(size_t i = 0; i < len; ++i) + { + str[i] = val; + } + } + +public: + + /** set the current substring to a copy of the given csubstr + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(void) copy_from(ro_substr that, size_t ifirst=0, size_t num=npos) + { + C4_ASSERT(ifirst >= 0 && ifirst <= len); + num = num != npos ? num : len - ifirst; + num = num < that.len ? num : that.len; + C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len); + // calling memcpy with null strings is undefined behavior + // and will wreak havoc in calling code's branches. + // see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637 + if(num) + memcpy(str + sizeof(C) * ifirst, that.str, sizeof(C) * num); + } + +public: + + /** reverse in place + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(void) reverse() + { + if(len == 0) return; + detail::_do_reverse(str, str + len - 1); + } + + /** revert a subpart in place + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(void) reverse_sub(size_t ifirst, size_t num) + { + C4_ASSERT(ifirst >= 0 && ifirst <= len); + C4_ASSERT(ifirst + num >= 0 && ifirst + num <= len); + if(num == 0) return; + detail::_do_reverse(str + ifirst, str + ifirst + num - 1); + } + + /** revert a range in place + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(void) reverse_range(size_t ifirst, size_t ilast) + { + C4_ASSERT(ifirst >= 0 && ifirst <= len); + C4_ASSERT(ilast >= 0 && ilast <= len); + if(ifirst == ilast) return; + detail::_do_reverse(str + ifirst, str + ilast - 1); + } + +public: + + /** erase part of the string. eg, with char s[] = "0123456789", + * substr(s).erase(3, 2) = "01256789", and s is now "01245678989" + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(basic_substring) erase(size_t pos, size_t num) + { + C4_ASSERT(pos >= 0 && pos+num <= len); + size_t num_to_move = len - pos - num; + memmove(str + pos, str + pos + num, sizeof(C) * num_to_move); + return basic_substring{str, len - num}; + } + + /** @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(basic_substring) erase_range(size_t first, size_t last) + { + C4_ASSERT(first <= last); + return erase(first, static_cast<size_t>(last-first)); + } + + /** erase a part of the string. + * @note @p sub must be a substring of this string + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(basic_substring) erase(ro_substr sub) + { + C4_ASSERT(is_super(sub)); + C4_ASSERT(sub.str >= str); + return erase(static_cast<size_t>(sub.str - str), sub.len); + } + +public: + + /** replace every occurrence of character @p value with the character @p repl + * @return the number of characters that were replaced + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(size_t) replace(C value, C repl, size_t pos=0) + { + C4_ASSERT((pos >= 0 && pos <= len) || pos == npos); + size_t did_it = 0; + while((pos = find(value, pos)) != npos) + { + str[pos++] = repl; + ++did_it; + } + return did_it; + } + + /** replace every occurrence of each character in @p value with + * the character @p repl. + * @return the number of characters that were replaced + * @note this method requires that the string memory is writeable and is SFINAEd out for const C */ + C4_REQUIRE_RW(size_t) replace(ro_substr chars, C repl, size_t pos=0) + { + C4_ASSERT((pos >= 0 && pos <= len) || pos == npos); + size_t did_it = 0; + while((pos = first_of(chars, pos)) != npos) + { + str[pos++] = repl; + ++did_it; + } + return did_it; + } + + /** replace @p pattern with @p repl, and write the result into + * @dst. pattern and repl don't need equal sizes. + * + * @return the required size for dst. No overflow occurs if + * dst.len is smaller than the required size; this can be used to + * determine the required size for an existing container. */ + size_t replace_all(rw_substr dst, ro_substr pattern, ro_substr repl, size_t pos=0) const + { + C4_ASSERT( ! pattern.empty()); //!< @todo relax this precondition + C4_ASSERT( ! this ->overlaps(dst)); //!< @todo relax this precondition + C4_ASSERT( ! pattern.overlaps(dst)); + C4_ASSERT( ! repl .overlaps(dst)); + C4_ASSERT((pos >= 0 && pos <= len) || pos == npos); + C4_SUPPRESS_WARNING_GCC_PUSH + C4_SUPPRESS_WARNING_GCC("-Warray-bounds") // gcc11 has a false positive here + #if (!defined(__clang__)) && (defined(__GNUC__) && (__GNUC__ >= 7)) + C4_SUPPRESS_WARNING_GCC("-Wstringop-overflow") // gcc11 has a false positive here + #endif + #define _c4append(first, last) \ + { \ + C4_ASSERT((last) >= (first)); \ + size_t num = static_cast<size_t>((last) - (first)); \ + if(num > 0 && sz + num <= dst.len) \ + { \ + memcpy(dst.str + sz, first, num * sizeof(C)); \ + } \ + sz += num; \ + } + size_t sz = 0; + size_t b = pos; + _c4append(str, str + pos); + do { + size_t e = find(pattern, b); + if(e == npos) + { + _c4append(str + b, str + len); + break; + } + _c4append(str + b, str + e); + _c4append(repl.begin(), repl.end()); + b = e + pattern.size(); + } while(b < len && b != npos); + return sz; + #undef _c4append + C4_SUPPRESS_WARNING_GCC_POP + } + + /** @} */ + +}; // template class basic_substring + + +#undef C4_REQUIRE_RW +#undef C4_REQUIRE_RO +#undef C4_NC2C + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** Because of a C++ limitation, substr cannot provide simultaneous + * overloads for constructing from a char[N] and a char*; the latter + * will always be chosen by the compiler. So this specialization is + * provided to simplify obtaining a substr from a char*. Being a + * function has the advantage of highlighting the strlen() cost. + * + * @see to_csubstr + * @see For a more detailed explanation on why the overloads cannot + * coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */ +inline substr to_substr(char *s) +{ + return substr(s, s ? strlen(s) : 0); +} + +/** Because of a C++ limitation, substr cannot provide simultaneous + * overloads for constructing from a char[N] and a char*; the latter + * will always be chosen by the compiler. So this specialization is + * provided to simplify obtaining a substr from a char*. Being a + * function has the advantage of highlighting the strlen() cost. + * + * @see to_substr + * @see For a more detailed explanation on why the overloads cannot + * coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */ +inline csubstr to_csubstr(char *s) +{ + return csubstr(s, s ? strlen(s) : 0); +} + +/** Because of a C++ limitation, substr cannot provide simultaneous + * overloads for constructing from a const char[N] and a const char*; + * the latter will always be chosen by the compiler. So this + * specialization is provided to simplify obtaining a substr from a + * char*. Being a function has the advantage of highlighting the + * strlen() cost. + * + * @overload to_csubstr + * @see to_substr + * @see For a more detailed explanation on why the overloads cannot + * coexist, see http://cplusplus.bordoon.com/specializeForCharacterArrays.html */ +inline csubstr to_csubstr(const char *s) +{ + return csubstr(s, s ? strlen(s) : 0); +} + + +/** neutral version for use in generic code */ +inline csubstr to_csubstr(csubstr s) +{ + return s; +} + +/** neutral version for use in generic code */ +inline csubstr to_csubstr(substr s) +{ + return s; +} + +/** neutral version for use in generic code */ +inline substr to_substr(substr s) +{ + return s; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<typename C, size_t N> inline bool operator== (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) == 0; } +template<typename C, size_t N> inline bool operator!= (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) != 0; } +template<typename C, size_t N> inline bool operator< (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) > 0; } +template<typename C, size_t N> inline bool operator> (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) < 0; } +template<typename C, size_t N> inline bool operator<= (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) >= 0; } +template<typename C, size_t N> inline bool operator>= (const C (&s)[N], basic_substring<C> const that) { return that.compare(s) <= 0; } + +template<typename C> inline bool operator== (C const c, basic_substring<C> const that) { return that.compare(c) == 0; } +template<typename C> inline bool operator!= (C const c, basic_substring<C> const that) { return that.compare(c) != 0; } +template<typename C> inline bool operator< (C const c, basic_substring<C> const that) { return that.compare(c) > 0; } +template<typename C> inline bool operator> (C const c, basic_substring<C> const that) { return that.compare(c) < 0; } +template<typename C> inline bool operator<= (C const c, basic_substring<C> const that) { return that.compare(c) >= 0; } +template<typename C> inline bool operator>= (C const c, basic_substring<C> const that) { return that.compare(c) <= 0; } + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** @define C4_SUBSTR_NO_OSTREAM_LSHIFT doctest does not deal well with + * template operator<< + * @see https://github.com/onqtam/doctest/pull/431 */ +#ifndef C4_SUBSTR_NO_OSTREAM_LSHIFT +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-conversion" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +/** output the string to a stream */ +template<class OStream, class C> +inline OStream& operator<< (OStream& os, basic_substring<C> s) +{ + os.write(s.str, s.len); + return os; +} + +// this causes ambiguity +///** this is used by google test */ +//template<class OStream, class C> +//inline void PrintTo(basic_substring<C> s, OStream* os) +//{ +// os->write(s.str, s.len); +//} + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +#endif // !C4_SUBSTR_NO_OSTREAM_LSHIFT + +} // namespace c4 + + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#endif /* _C4_SUBSTR_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/substr_fwd.hpp b/thirdparty/ryml/ext/c4core/src/c4/substr_fwd.hpp new file mode 100644 index 000000000..63d01b595 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/substr_fwd.hpp @@ -0,0 +1,16 @@ +#ifndef _C4_SUBSTR_FWD_HPP_ +#define _C4_SUBSTR_FWD_HPP_ + +#include "c4/export.hpp" + +namespace c4 { + +#ifndef DOXYGEN +template<class C> struct basic_substring; +using csubstr = C4CORE_EXPORT basic_substring<const char>; +using substr = C4CORE_EXPORT basic_substring<char>; +#endif // !DOXYGEN + +} // namespace c4 + +#endif /* _C4_SUBSTR_FWD_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/szconv.hpp b/thirdparty/ryml/ext/c4core/src/c4/szconv.hpp new file mode 100644 index 000000000..76cec1146 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/szconv.hpp @@ -0,0 +1,64 @@ +#ifndef _C4_SZCONV_HPP_ +#define _C4_SZCONV_HPP_ + +/** @file szconv.hpp utilities to deal safely with narrowing conversions */ + +#include "c4/config.hpp" +#include "c4/error.hpp" + +#include <limits> + +namespace c4 { + +/** @todo this would be so much easier with calls to numeric_limits::max()... */ +template<class SizeOut, class SizeIn> +struct is_narrower_size : std::conditional +< + (std::is_signed<SizeOut>::value == std::is_signed<SizeIn>::value) + ? + (sizeof(SizeOut) < sizeof(SizeIn)) + : + ( + (sizeof(SizeOut) < sizeof(SizeIn)) + || + ( + (sizeof(SizeOut) == sizeof(SizeIn)) + && + (std::is_signed<SizeOut>::value && std::is_unsigned<SizeIn>::value) + ) + ), + std::true_type, + std::false_type +>::type +{ + static_assert(std::is_integral<SizeIn >::value, "must be integral type"); + static_assert(std::is_integral<SizeOut>::value, "must be integral type"); +}; + + +/** when SizeOut is wider than SizeIn, assignment can occur without reservations */ +template<class SizeOut, class SizeIn> +C4_ALWAYS_INLINE +typename std::enable_if< ! is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type +szconv(SizeIn sz) noexcept +{ + return static_cast<SizeOut>(sz); +} + +/** when SizeOut is narrower than SizeIn, narrowing will occur, so we check + * for overflow. Note that this check is done only if C4_XASSERT is enabled. + * @see C4_XASSERT */ +template<class SizeOut, class SizeIn> +C4_ALWAYS_INLINE +typename std::enable_if<is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type +szconv(SizeIn sz) C4_NOEXCEPT_X +{ + C4_XASSERT(sz >= 0); + C4_XASSERT_MSG((SizeIn)sz <= (SizeIn)std::numeric_limits<SizeOut>::max(), "size conversion overflow: in=%zu", (size_t)sz); + SizeOut szo = static_cast<SizeOut>(sz); + return szo; +} + +} // namespace c4 + +#endif /* _C4_SZCONV_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/type_name.hpp b/thirdparty/ryml/ext/c4core/src/c4/type_name.hpp new file mode 100644 index 000000000..41f13562b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/type_name.hpp @@ -0,0 +1,125 @@ +#ifndef _C4_TYPENAME_HPP_ +#define _C4_TYPENAME_HPP_ + +/** @file type_name.hpp compile-time type name */ + +#include "c4/span.hpp" + +/// @cond dev +struct _c4t +{ + const char *str; + size_t sz; + template<size_t N> + constexpr _c4t(const char (&s)[N]) : str(s), sz(N-1) {} // take off the \0 +}; +// this is a more abbreviated way of getting the type name +// (if we used span in the return type, the name would involve +// templates and would create longer type name strings, +// as well as larger differences between compilers) +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE +_c4t _c4tn() +{ + auto p = _c4t(C4_PRETTY_FUNC); + return p; +} +/// @endcond + + +namespace c4 { + +/** compile-time type name + * @see http://stackoverflow.com/a/20170989/5875572 */ +template<class T> +C4_CONSTEXPR14 cspan<char> type_name() +{ + const _c4t p = _c4tn<T>(); + +#if (0) // _C4_THIS_IS_A_DEBUG_SCAFFOLD + for(size_t index = 0; index < p.sz; ++index) + { + printf(" %2c", p.str[index]); + } + printf("\n"); + for(size_t index = 0; index < p.sz; ++index) + { + printf(" %2d", (int)index); + } + printf("\n"); +#endif + +#if defined(_MSC_VER) +# if defined(__clang__) // Visual Studio has the clang toolset + // example: + // ..........................xxx. + // _c4t __cdecl _c4tn() [T = int] + enum : size_t { tstart = 26, tend = 1}; + +# elif defined(C4_MSVC_2015) || defined(C4_MSVC_2017) || defined(C4_MSVC_2019) || defined(C4_MSVC_2022) + // Note: subtract 7 at the end because the function terminates with ">(void)" in VS2015+ + cspan<char>::size_type tstart = 26, tend = 7; + + const char *s = p.str + tstart; // look at the start + + // we're not using strcmp() or memcmp() to spare the #include + + // does it start with 'class '? + if(p.sz > 6 && s[0] == 'c' && s[1] == 'l' && s[2] == 'a' && s[3] == 's' && s[4] == 's' && s[5] == ' ') + { + tstart += 6; + } + // does it start with 'struct '? + else if(p.sz > 7 && s[0] == 's' && s[1] == 't' && s[2] == 'r' && s[3] == 'u' && s[4] == 'c' && s[5] == 't' && s[6] == ' ') + { + tstart += 7; + } + +# else + C4_NOT_IMPLEMENTED(); +# endif + +#elif defined(__ICC) + // example: + // ........................xxx. + // "_c4t _c4tn() [with T = int]" + enum : size_t { tstart = 23, tend = 1}; + +#elif defined(__clang__) + // example: + // ...................xxx. + // "_c4t _c4tn() [T = int]" + enum : size_t { tstart = 18, tend = 1}; + +#elif defined(__GNUC__) + #if __GNUC__ >= 7 && C4_CPP >= 14 + // example: + // ..................................xxx. + // "constexpr _c4t _c4tn() [with T = int]" + enum : size_t { tstart = 33, tend = 1 }; + #else + // example: + // ........................xxx. + // "_c4t _c4tn() [with T = int]" + enum : size_t { tstart = 23, tend = 1 }; + #endif +#else + C4_NOT_IMPLEMENTED(); +#endif + + cspan<char> o(p.str + tstart, p.sz - tstart - tend); + + return o; +} + +/** compile-time type name + * @overload */ +template<class T> +C4_CONSTEXPR14 C4_ALWAYS_INLINE cspan<char> type_name(T const&) +{ + return type_name<T>(); +} + +} // namespace c4 + +#endif //_C4_TYPENAME_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/types.hpp b/thirdparty/ryml/ext/c4core/src/c4/types.hpp new file mode 100644 index 000000000..394a808fd --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/types.hpp @@ -0,0 +1,492 @@ +#ifndef _C4_TYPES_HPP_ +#define _C4_TYPES_HPP_ + +#include <stdint.h> +#include <stddef.h> +#include <type_traits> + +#if __cplusplus >= 201103L +#include <utility> // for integer_sequence and friends +#endif + +#include "c4/preprocessor.hpp" +#include "c4/language.hpp" + +/** @file types.hpp basic types, and utility macros and traits for types. + * @ingroup basic_headers */ + +/** @defgroup types Type utilities */ + +namespace c4 { + +/** @defgroup intrinsic_types Intrinsic types + * @ingroup types + * @{ */ + +using cbyte = const char; /**< a constant byte */ +using byte = char; /**< a mutable byte */ + +using i8 = int8_t; +using i16 = int16_t; +using i32 = int32_t; +using i64 = int64_t; +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; +using u64 = uint64_t; + +using f32 = float; +using f64 = double; + +using ssize_t = typename std::make_signed<size_t>::type; + +/** @} */ + +//-------------------------------------------------- + +/** @defgroup utility_types Utility types + * @ingroup types + * @{ */ + +// some tag types + +/** a tag type for initializing the containers with variadic arguments a la + * initializer_list, minus the initializer_list overload problems. + */ +struct aggregate_t {}; +/** @see aggregate_t */ +constexpr const aggregate_t aggregate{}; + +/** a tag type for specifying the initial capacity of allocatable contiguous storage */ +struct with_capacity_t {}; +/** @see with_capacity_t */ +constexpr const with_capacity_t with_capacity{}; + +/** a tag type for disambiguating template parameter packs in variadic template overloads */ +struct varargs_t {}; +/** @see with_capacity_t */ +constexpr const varargs_t varargs{}; + + +//-------------------------------------------------- + +/** whether a value should be used in place of a const-reference in argument passing. */ +template<class T> +struct cref_uses_val +{ + enum { value = ( + std::is_scalar<T>::value + || + ( +#if C4_CPP >= 20 + (std::is_trivially_copyable<T>::value && std::is_standard_layout<T>::value) +#else + std::is_pod<T>::value +#endif + && + sizeof(T) <= sizeof(size_t))) }; +}; +/** utility macro to override the default behaviour for c4::fastcref<T> + @see fastcref */ +#define C4_CREF_USES_VAL(T) \ +template<> \ +struct cref_uses_val<T> \ +{ \ + enum { value = true }; \ +}; + +/** Whether to use pass-by-value or pass-by-const-reference in a function argument + * or return type. */ +template<class T> +using fastcref = typename std::conditional<c4::cref_uses_val<T>::value, T, T const&>::type; + +//-------------------------------------------------- + +/** Just what its name says. Useful sometimes as a default empty policy class. */ +struct EmptyStruct +{ + template<class... T> EmptyStruct(T && ...){} +}; + +/** Just what its name says. Useful sometimes as a default policy class to + * be inherited from. */ +struct EmptyStructVirtual +{ + virtual ~EmptyStructVirtual() = default; + template<class... T> EmptyStructVirtual(T && ...){} +}; + + +/** */ +template<class T> +struct inheritfrom : public T {}; + +//-------------------------------------------------- +// Utilities to make a class obey size restrictions (eg, min size or size multiple of). +// DirectX usually makes this restriction with uniform buffers. +// This is also useful for padding to prevent false-sharing. + +/** how many bytes must be added to size such that the result is at least minsize? */ +C4_ALWAYS_INLINE constexpr size_t min_remainder(size_t size, size_t minsize) noexcept +{ + return size < minsize ? minsize-size : 0; +} + +/** how many bytes must be added to size such that the result is a multiple of multipleof? */ +C4_ALWAYS_INLINE constexpr size_t mult_remainder(size_t size, size_t multipleof) noexcept +{ + return (((size % multipleof) != 0) ? (multipleof-(size % multipleof)) : 0); +} + +/* force the following class to be tightly packed. */ +#pragma pack(push, 1) +/** pad a class with more bytes at the end. + * @see http://stackoverflow.com/questions/21092415/force-c-structure-to-pack-tightly */ +template<class T, size_t BytesToPadAtEnd> +struct Padded : public T +{ + using T::T; + using T::operator=; + Padded(T const& val) : T(val) {} + Padded(T && val) : T(val) {} + char ___c4padspace___[BytesToPadAtEnd]; +}; +#pragma pack(pop) +/** When the padding argument is 0, we cannot declare the char[] array. */ +template<class T> +struct Padded<T, 0> : public T +{ + using T::T; + using T::operator=; + Padded(T const& val) : T(val) {} + Padded(T && val) : T(val) {} +}; + +/** make T have a size which is at least Min bytes */ +template<class T, size_t Min> +using MinSized = Padded<T, min_remainder(sizeof(T), Min)>; + +/** make T have a size which is a multiple of Mult bytes */ +template<class T, size_t Mult> +using MultSized = Padded<T, mult_remainder(sizeof(T), Mult)>; + +/** make T have a size which is simultaneously: + * -bigger or equal than Min + * -a multiple of Mult */ +template<class T, size_t Min, size_t Mult> +using MinMultSized = MultSized<MinSized<T, Min>, Mult>; + +/** make T be suitable for use as a uniform buffer. (at least with DirectX). */ +template<class T> +using UbufSized = MinMultSized<T, 64, 16>; + + +//----------------------------------------------------------------------------- + +#define C4_NO_COPY_CTOR(ty) ty(ty const&) = delete +#define C4_NO_MOVE_CTOR(ty) ty(ty &&) = delete +#define C4_NO_COPY_ASSIGN(ty) ty& operator=(ty const&) = delete +#define C4_NO_MOVE_ASSIGN(ty) ty& operator=(ty &&) = delete +#define C4_DEFAULT_COPY_CTOR(ty) ty(ty const&) noexcept = default +#define C4_DEFAULT_MOVE_CTOR(ty) ty(ty &&) noexcept = default +#define C4_DEFAULT_COPY_ASSIGN(ty) ty& operator=(ty const&) noexcept = default +#define C4_DEFAULT_MOVE_ASSIGN(ty) ty& operator=(ty &&) noexcept = default + +#define C4_NO_COPY_OR_MOVE_CTOR(ty) \ + C4_NO_COPY_CTOR(ty); \ + C4_NO_MOVE_CTOR(ty) + +#define C4_NO_COPY_OR_MOVE_ASSIGN(ty) \ + C4_NO_COPY_ASSIGN(ty); \ + C4_NO_MOVE_ASSIGN(ty) + +#define C4_NO_COPY_OR_MOVE(ty) \ + C4_NO_COPY_OR_MOVE_CTOR(ty); \ + C4_NO_COPY_OR_MOVE_ASSIGN(ty) + +#define C4_DEFAULT_COPY_AND_MOVE_CTOR(ty) \ + C4_DEFAULT_COPY_CTOR(ty); \ + C4_DEFAULT_MOVE_CTOR(ty) + +#define C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty) \ + C4_DEFAULT_COPY_ASSIGN(ty); \ + C4_DEFAULT_MOVE_ASSIGN(ty) + +#define C4_DEFAULT_COPY_AND_MOVE(ty) \ + C4_DEFAULT_COPY_AND_MOVE_CTOR(ty); \ + C4_DEFAULT_COPY_AND_MOVE_ASSIGN(ty) + +/** @see https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable */ +#define C4_MUST_BE_TRIVIAL_COPY(ty) \ + static_assert(std::is_trivially_copyable<ty>::value, #ty " must be trivially copyable") + +/** @} */ + + +//----------------------------------------------------------------------------- + +/** @defgroup traits_types Type traits utilities + * @ingroup types + * @{ */ + +// http://stackoverflow.com/questions/10821380/is-t-an-instance-of-a-template-in-c +template<template<typename...> class X, typename T> struct is_instance_of_tpl : std::false_type {}; +template<template<typename...> class X, typename... Y> struct is_instance_of_tpl<X, X<Y...>> : std::true_type {}; + +//----------------------------------------------------------------------------- + +/** SFINAE. use this macro to enable a template function overload +based on a compile-time condition. +@code +// define an overload for a non-pod type +template<class T, C4_REQUIRE_T(std::is_pod<T>::value)> +void foo() { std::cout << "pod type\n"; } + +// define an overload for a non-pod type +template<class T, C4_REQUIRE_T(!std::is_pod<T>::value)> +void foo() { std::cout << "nonpod type\n"; } + +struct non_pod +{ + non_pod() : name("asdfkjhasdkjh") {} + const char *name; +}; + +int main() +{ + foo<float>(); // prints "pod type" + foo<non_pod>(); // prints "nonpod type" +} +@endcode */ +#define C4_REQUIRE_T(cond) typename std::enable_if<cond, bool>::type* = nullptr + +/** enable_if for a return type + * @see C4_REQUIRE_T */ +#define C4_REQUIRE_R(cond, type_) typename std::enable_if<cond, type_>::type + +//----------------------------------------------------------------------------- +/** define a traits class reporting whether a type provides a member typedef */ +#define C4_DEFINE_HAS_TYPEDEF(member_typedef) \ +template<typename T> \ +struct has_##stype \ +{ \ +private: \ + \ + typedef char yes; \ + typedef struct { char array[2]; } no; \ + \ + template<typename C> \ + static yes _test(typename C::member_typedef*); \ + \ + template<typename C> \ + static no _test(...); \ + \ +public: \ + \ + enum { value = (sizeof(_test<T>(0)) == sizeof(yes)) }; \ + \ +} + + +/** @} */ + + +//----------------------------------------------------------------------------- + + +/** @defgroup type_declarations Type declaration utilities + * @ingroup types + * @{ */ + +#define _c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I) \ + \ + using size_type = I; \ + using ssize_type = typename std::make_signed<I>::type; \ + using difference_type = typename std::make_signed<I>::type; \ + \ + using value_type = T; \ + using pointer = T*; \ + using const_pointer = T const*; \ + using reference = T&; \ + using const_reference = T const& + +#define _c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I) \ + \ + using size_type = I; \ + using ssize_type = typename std::make_signed<I>::type; \ + using difference_type = typename std::make_signed<I>::type; \ + \ + template<I n> using value_type = typename std::tuple_element< n, std::tuple<interior_types...>>::type; \ + template<I n> using pointer = value_type<n>*; \ + template<I n> using const_pointer = value_type<n> const*; \ + template<I n> using reference = value_type<n>&; \ + template<I n> using const_reference = value_type<n> const& + + +#define _c4_DEFINE_ARRAY_TYPES(T, I) \ + \ + _c4_DEFINE_ARRAY_TYPES_WITHOUT_ITERATOR(T, I); \ + \ + using iterator = T*; \ + using const_iterator = T const*; \ + using reverse_iterator = std::reverse_iterator<T*>; \ + using const_reverse_iterator = std::reverse_iterator<T const*> + + +#define _c4_DEFINE_TUPLE_ARRAY_TYPES(interior_types, I) \ + \ + _c4_DEFINE_TUPLE_ARRAY_TYPES_WITHOUT_ITERATOR(interior_types, I); \ + \ + template<I n> using iterator = value_type<n>*; \ + template<I n> using const_iterator = value_type<n> const*; \ + template<I n> using reverse_iterator = std::reverse_iterator< value_type<n>*>; \ + template<I n> using const_reverse_iterator = std::reverse_iterator< value_type<n> const*> + + + +/** @} */ + + +//----------------------------------------------------------------------------- + + +/** @defgroup compatility_utilities Backport implementation of some Modern C++ utilities + * @ingroup types + * @{ */ + +//----------------------------------------------------------------------------- +// index_sequence and friends are available only for C++14 and later. +// A C++11 implementation is provided here. +// This implementation was copied over from clang. +// see http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 + +#if __cplusplus > 201103L + +using std::integer_sequence; +using std::index_sequence; +using std::make_integer_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +/** C++11 implementation of integer sequence + * @see https://en.cppreference.com/w/cpp/utility/integer_sequence + * @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */ +template<class _Tp, _Tp... _Ip> +struct integer_sequence +{ + static_assert(std::is_integral<_Tp>::value, + "std::integer_sequence can only be instantiated with an integral type" ); + using value_type = _Tp; + static constexpr size_t size() noexcept { return sizeof...(_Ip); } +}; + +/** C++11 implementation of index sequence + * @see https://en.cppreference.com/w/cpp/utility/integer_sequence + * @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */ +template<size_t... _Ip> +using index_sequence = integer_sequence<size_t, _Ip...>; + +/** @cond DONT_DOCUMENT_THIS */ +namespace __detail { + +template<typename _Tp, size_t ..._Extra> +struct __repeat; + +template<typename _Tp, _Tp ..._Np, size_t ..._Extra> +struct __repeat<integer_sequence<_Tp, _Np...>, _Extra...> +{ + using type = integer_sequence<_Tp, + _Np..., + sizeof...(_Np) + _Np..., + 2 * sizeof...(_Np) + _Np..., + 3 * sizeof...(_Np) + _Np..., + 4 * sizeof...(_Np) + _Np..., + 5 * sizeof...(_Np) + _Np..., + 6 * sizeof...(_Np) + _Np..., + 7 * sizeof...(_Np) + _Np..., + _Extra...>; +}; + +template<size_t _Np> struct __parity; +template<size_t _Np> struct __make : __parity<_Np % 8>::template __pmake<_Np> {}; + +template<> struct __make<0> { using type = integer_sequence<size_t>; }; +template<> struct __make<1> { using type = integer_sequence<size_t, 0>; }; +template<> struct __make<2> { using type = integer_sequence<size_t, 0, 1>; }; +template<> struct __make<3> { using type = integer_sequence<size_t, 0, 1, 2>; }; +template<> struct __make<4> { using type = integer_sequence<size_t, 0, 1, 2, 3>; }; +template<> struct __make<5> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4>; }; +template<> struct __make<6> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5>; }; +template<> struct __make<7> { using type = integer_sequence<size_t, 0, 1, 2, 3, 4, 5, 6>; }; + +template<> struct __parity<0> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type> {}; }; +template<> struct __parity<1> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 1> {}; }; +template<> struct __parity<2> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 2, _Np - 1> {}; }; +template<> struct __parity<3> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 3, _Np - 2, _Np - 1> {}; }; +template<> struct __parity<4> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; }; +template<> struct __parity<5> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; }; +template<> struct __parity<6> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; }; +template<> struct __parity<7> { template<size_t _Np> struct __pmake : __repeat<typename __make<_Np / 8>::type, _Np - 7, _Np - 6, _Np - 5, _Np - 4, _Np - 3, _Np - 2, _Np - 1> {}; }; + +template<typename _Tp, typename _Up> +struct __convert +{ + template<typename> struct __result; + template<_Tp ..._Np> struct __result<integer_sequence<_Tp, _Np...>> + { + using type = integer_sequence<_Up, _Np...>; + }; +}; + +template<typename _Tp> +struct __convert<_Tp, _Tp> +{ + template<typename _Up> struct __result + { + using type = _Up; + }; +}; + +template<typename _Tp, _Tp _Np> +using __make_integer_sequence_unchecked = typename __detail::__convert<size_t, _Tp>::template __result<typename __detail::__make<_Np>::type>::type; + +template<class _Tp, _Tp _Ep> +struct __make_integer_sequence +{ + static_assert(std::is_integral<_Tp>::value, + "std::make_integer_sequence can only be instantiated with an integral type" ); + static_assert(0 <= _Ep, "std::make_integer_sequence input shall not be negative"); + typedef __make_integer_sequence_unchecked<_Tp, _Ep> type; +}; + +} // namespace __detail +/** @endcond */ + + +/** C++11 implementation of index sequence + * @see https://en.cppreference.com/w/cpp/utility/integer_sequence + * @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */ +template<class _Tp, _Tp _Np> +using make_integer_sequence = typename __detail::__make_integer_sequence<_Tp, _Np>::type; + +/** C++11 implementation of index sequence + * @see https://en.cppreference.com/w/cpp/utility/integer_sequence + * @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */ +template<size_t _Np> +using make_index_sequence = make_integer_sequence<size_t, _Np>; + +/** C++11 implementation of index sequence + * @see https://en.cppreference.com/w/cpp/utility/integer_sequence + * @see taken from clang: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/utility?revision=211563&view=markup#l687 */ +template<class... _Tp> +using index_sequence_for = make_index_sequence<sizeof...(_Tp)>; +#endif + +/** @} */ + + +} // namespace c4 + +#endif /* _C4_TYPES_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/unrestrict.hpp b/thirdparty/ryml/ext/c4core/src/c4/unrestrict.hpp new file mode 100644 index 000000000..552f9ccff --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/unrestrict.hpp @@ -0,0 +1,17 @@ +#ifdef _C4_RESTRICT_HPP_ // must match the include guard from restrict.hpp + +/** @file unrestrict.hpp cleans up restrict macros */ + +#undef $ +#undef $$ +#undef c$ +#undef c$$ + +#undef _C4_RESTRICT_HPP_ + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +#endif + +#endif /* _C4_RESTRICT_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/utf.cpp b/thirdparty/ryml/ext/c4core/src/c4/utf.cpp new file mode 100644 index 000000000..f1d509015 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/utf.cpp @@ -0,0 +1,56 @@ +#include "c4/utf.hpp" +#include "c4/charconv.hpp" + +namespace c4 { + +size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code) +{ + C4_UNUSED(buflen); + C4_ASSERT(buflen >= 4); + if (code <= UINT32_C(0x7f)) + { + buf[0] = (uint8_t)code; + return 1u; + } + else if(code <= UINT32_C(0x7ff)) + { + buf[0] = (uint8_t)(UINT32_C(0xc0) | (code >> 6)); /* 110xxxxx */ + buf[1] = (uint8_t)(UINT32_C(0x80) | (code & UINT32_C(0x3f))); /* 10xxxxxx */ + return 2u; + } + else if(code <= UINT32_C(0xffff)) + { + buf[0] = (uint8_t)(UINT32_C(0xe0) | ((code >> 12))); /* 1110xxxx */ + buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */ + buf[2] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */ + return 3u; + } + else if(code <= UINT32_C(0x10ffff)) + { + buf[0] = (uint8_t)(UINT32_C(0xf0) | ((code >> 18))); /* 11110xxx */ + buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 12) & UINT32_C(0x3f))); /* 10xxxxxx */ + buf[2] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */ + buf[3] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */ + return 4u; + } + return 0; +} + +substr decode_code_point(substr out, csubstr code_point) +{ + C4_ASSERT(out.len >= 4); + C4_ASSERT(!code_point.begins_with("U+")); + C4_ASSERT(!code_point.begins_with("\\x")); + C4_ASSERT(!code_point.begins_with("\\u")); + C4_ASSERT(!code_point.begins_with("\\U")); + C4_ASSERT(!code_point.begins_with('0')); + C4_ASSERT(code_point.len <= 8); + C4_ASSERT(code_point.len > 0); + uint32_t code_point_val; + C4_CHECK(read_hex(code_point, &code_point_val)); + size_t ret = decode_code_point((uint8_t*)out.str, out.len, code_point_val); + C4_ASSERT(ret <= 4); + return out.first(ret); +} + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/src/c4/utf.hpp b/thirdparty/ryml/ext/c4core/src/c4/utf.hpp new file mode 100644 index 000000000..116b20593 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/utf.hpp @@ -0,0 +1,16 @@ +#ifndef C4_UTF_HPP_ +#define C4_UTF_HPP_ + +#include "c4/language.hpp" +#include "c4/substr_fwd.hpp" +#include <stddef.h> +#include <stdint.h> + +namespace c4 { + +substr decode_code_point(substr out, csubstr code_point); +size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code); + +} // namespace c4 + +#endif // C4_UTF_HPP_ diff --git a/thirdparty/ryml/ext/c4core/src/c4/windows.hpp b/thirdparty/ryml/ext/c4core/src/c4/windows.hpp new file mode 100644 index 000000000..d94c66c55 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/windows.hpp @@ -0,0 +1,10 @@ +#ifndef _C4_WINDOWS_HPP_ +#define _C4_WINDOWS_HPP_ + +#if defined(_WIN64) || defined(_WIN32) +#include "c4/windows_push.hpp" +#include <windows.h> +#include "c4/windows_pop.hpp" +#endif + +#endif /* _C4_WINDOWS_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/windows_pop.hpp b/thirdparty/ryml/ext/c4core/src/c4/windows_pop.hpp new file mode 100644 index 000000000..e055af6fa --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/windows_pop.hpp @@ -0,0 +1,41 @@ +#ifndef _C4_WINDOWS_POP_HPP_ +#define _C4_WINDOWS_POP_HPP_ + +#if defined(_WIN64) || defined(_WIN32) + +#ifdef _c4_AMD64_ +# undef _c4_AMD64_ +# undef _AMD64_ +#endif +#ifdef _c4_X86_ +# undef _c4_X86_ +# undef _X86_ +#endif +#ifdef _c4_ARM_ +# undef _c4_ARM_ +# undef _ARM_ +#endif + +#ifdef _c4_NOMINMAX +# undef _c4_NOMINMAX +# undef NOMINMAX +#endif + +#ifdef NOGDI +# undef _c4_NOGDI +# undef NOGDI +#endif + +#ifdef VC_EXTRALEAN +# undef _c4_VC_EXTRALEAN +# undef VC_EXTRALEAN +#endif + +#ifdef WIN32_LEAN_AND_MEAN +# undef _c4_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif /* defined(_WIN64) || defined(_WIN32) */ + +#endif /* _C4_WINDOWS_POP_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/src/c4/windows_push.hpp b/thirdparty/ryml/ext/c4core/src/c4/windows_push.hpp new file mode 100644 index 000000000..156fe2fb4 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/src/c4/windows_push.hpp @@ -0,0 +1,102 @@ +#ifndef _C4_WINDOWS_PUSH_HPP_ +#define _C4_WINDOWS_PUSH_HPP_ + +/** @file windows_push.hpp sets up macros to include windows header files + * without pulling in all of <windows.h> + * + * @see #include windows_pop.hpp to undefine these macros + * + * @see https://aras-p.info/blog/2018/01/12/Minimizing-windows.h/ */ + + +#if defined(_WIN64) || defined(_WIN32) + +#if defined(_M_AMD64) +# ifndef _AMD64_ +# define _c4_AMD64_ +# define _AMD64_ +# endif +#elif defined(_M_IX86) +# ifndef _X86_ +# define _c4_X86_ +# define _X86_ +# endif +#elif defined(_M_ARM64) +# ifndef _ARM64_ +# define _c4_ARM64_ +# define _ARM64_ +# endif +#elif defined(_M_ARM) +# ifndef _ARM_ +# define _c4_ARM_ +# define _ARM_ +# endif +#endif + +#ifndef NOMINMAX +# define _c4_NOMINMAX +# define NOMINMAX +#endif + +#ifndef NOGDI +# define _c4_NOGDI +# define NOGDI +#endif + +#ifndef VC_EXTRALEAN +# define _c4_VC_EXTRALEAN +# define VC_EXTRALEAN +#endif + +#ifndef WIN32_LEAN_AND_MEAN +# define _c4_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +/* If defined, the following flags inhibit definition + * of the indicated items. + * + * NOGDICAPMASKS - CC_*, LC_*, PC_*, CP_*, TC_*, RC_ + * NOVIRTUALKEYCODES - VK_* + * NOWINMESSAGES - WM_*, EM_*, LB_*, CB_* + * NOWINSTYLES - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_* + * NOSYSMETRICS - SM_* + * NOMENUS - MF_* + * NOICONS - IDI_* + * NOKEYSTATES - MK_* + * NOSYSCOMMANDS - SC_* + * NORASTEROPS - Binary and Tertiary raster ops + * NOSHOWWINDOW - SW_* + * OEMRESOURCE - OEM Resource values + * NOATOM - Atom Manager routines + * NOCLIPBOARD - Clipboard routines + * NOCOLOR - Screen colors + * NOCTLMGR - Control and Dialog routines + * NODRAWTEXT - DrawText() and DT_* + * NOGDI - All GDI defines and routines + * NOKERNEL - All KERNEL defines and routines + * NOUSER - All USER defines and routines + * NONLS - All NLS defines and routines + * NOMB - MB_* and MessageBox() + * NOMEMMGR - GMEM_*, LMEM_*, GHND, LHND, associated routines + * NOMETAFILE - typedef METAFILEPICT + * NOMINMAX - Macros min(a,b) and max(a,b) + * NOMSG - typedef MSG and associated routines + * NOOPENFILE - OpenFile(), OemToAnsi, AnsiToOem, and OF_* + * NOSCROLL - SB_* and scrolling routines + * NOSERVICE - All Service Controller routines, SERVICE_ equates, etc. + * NOSOUND - Sound driver routines + * NOTEXTMETRIC - typedef TEXTMETRIC and associated routines + * NOWH - SetWindowsHook and WH_* + * NOWINOFFSETS - GWL_*, GCL_*, associated routines + * NOCOMM - COMM driver routines + * NOKANJI - Kanji support stuff. + * NOHELP - Help engine interface. + * NOPROFILER - Profiler interface. + * NODEFERWINDOWPOS - DeferWindowPos routines + * NOMCX - Modem Configuration Extensions + */ + +#endif /* defined(_WIN64) || defined(_WIN32) */ + +#endif /* _C4_WINDOWS_PUSH_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/tbump.toml b/thirdparty/ryml/ext/c4core/tbump.toml new file mode 100644 index 000000000..310c606d6 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/tbump.toml @@ -0,0 +1,48 @@ +# Uncomment this if your project is hosted on GitHub: +# github_url = "https://github.com/<user or organization>/<project>/" + +[version] +current = "0.1.11" + +# Example of a semver regexp. +# Make sure this matches current_version before +# using tbump +regex = ''' + (?P<major>\d+) + \. + (?P<minor>\d+) + \. + (?P<patch>\d+) + .* + ''' + +[git] +message_template = "Bump to {new_version}" +tag_template = "v{new_version}" + +# For each file to patch, add a [[file]] config +# section containing the path of the file, relative to the +# tbump.toml location. +[[file]] +src = "CMakeLists.txt" +search = "c4_project\\(VERSION {current_version}" +[[file]] +src = "test/test_install/CMakeLists.txt" +search = "c4_project\\(VERSION {current_version}" +[[file]] +src = "test/test_singleheader/CMakeLists.txt" +search = "c4_project\\(VERSION {current_version}" + +# You can specify a list of commands to +# run after the files have been patched +# and before the git commit is made + +# [[before_commit]] +# name = "check changelog" +# cmd = "grep -q {new_version} Changelog.rst" + +# Or run some commands after the git tag and the branch +# have been pushed: +# [[after_push]] +# name = "publish" +# cmd = "./publish.sh" diff --git a/thirdparty/ryml/ext/c4core/test/c4/libtest/archetypes.cpp b/thirdparty/ryml/ext/c4core/test/c4/libtest/archetypes.cpp new file mode 100644 index 000000000..9c4fb4310 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/c4/libtest/archetypes.cpp @@ -0,0 +1,9 @@ +#include "c4/libtest/archetypes.hpp"
+
+namespace c4 {
+namespace archetypes {
+
+int IdOwner::s_current = 0;
+
+} // namespace archetypes
+} // namespace c4
diff --git a/thirdparty/ryml/ext/c4core/test/c4/libtest/archetypes.hpp b/thirdparty/ryml/ext/c4core/test/c4/libtest/archetypes.hpp new file mode 100644 index 000000000..87a4644e9 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/c4/libtest/archetypes.hpp @@ -0,0 +1,551 @@ +#ifndef _C4_TEST_ARCHETYPES_HPP_ +#define _C4_TEST_ARCHETYPES_HPP_ + +#ifdef C4CORE_SINGLE_HEADER +#include "c4/c4core_all.hpp" +#else +#include "c4/memory_resource.hpp" +#include "c4/allocator.hpp" +#include "c4/char_traits.hpp" +#endif +#include "c4/test.hpp" + +#include <vector> +#include <string> +#include <array> + +namespace c4 { + +template< class String > class sstream; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +namespace archetypes { + +template< class T > void check_archetype(T const& a) { a.check(); } +template< class T > void check_archetype(T const& a, T const& ref) { a.check(ref); } +inline void check_archetype(char ) {} +inline void check_archetype(wchar_t ) {} +inline void check_archetype(int8_t ) {} +inline void check_archetype(uint8_t ) {} +inline void check_archetype(int16_t ) {} +inline void check_archetype(uint16_t) {} +inline void check_archetype(int32_t ) {} +inline void check_archetype(uint32_t) {} +inline void check_archetype(int64_t ) {} +inline void check_archetype(uint64_t) {} +inline void check_archetype(float ) {} +inline void check_archetype(double ) {} +inline void check_archetype(char a, char ref) { CHECK_EQ(a, ref); } +inline void check_archetype(wchar_t a, wchar_t ref) { CHECK_EQ(a, ref); } +inline void check_archetype(int8_t a, int8_t ref) { CHECK_EQ(a, ref); } +inline void check_archetype(uint8_t a, uint8_t ref) { CHECK_EQ(a, ref); } +inline void check_archetype(int16_t a, int16_t ref) { CHECK_EQ(a, ref); } +inline void check_archetype(uint16_t a, uint16_t ref) { CHECK_EQ(a, ref); } +inline void check_archetype(int32_t a, int32_t ref) { CHECK_EQ(a, ref); } +inline void check_archetype(uint32_t a, uint32_t ref) { CHECK_EQ(a, ref); } +inline void check_archetype(int64_t a, int64_t ref) { CHECK_EQ(a, ref); } +inline void check_archetype(uint64_t a, uint64_t ref) { CHECK_EQ(a, ref); } +inline void check_archetype(float a, float ref) { CHECK_EQ((double)a, doctest::Approx((double)ref)); } +inline void check_archetype(double a, double ref) { CHECK_EQ(a, doctest::Approx(ref)); } + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template< class T, class Proto > +struct archetype_proto_base +{ + static T const& get(size_t which) + { + auto const& a = Proto::arr(); + C4_ASSERT(which < (int)a.size()); + return a[which]; + } + static std::array< T, 8 > dup() + { + std::array< T, 8 > d = Proto::arr; + return d; + } + static std::array< Counting<T>, 8 > cdup() + { + std::array< Counting<T>, 8 > d = Proto::carr; + return d; + } + static std::vector< T > dup(size_t n) + { + auto const& a = Proto::arr(); + std::vector< T > d; + d.reserve(n); + for(size_t i = 0, pos = 0; i < n; ++i, pos = ((pos+1)%a.size())) + { + d.push_back(a[pos]); + } + return d; + } + static std::vector< Counting<T> > cdup(size_t n) + { + auto const& a = Proto::arr(); + std::vector< Counting<T> > d; + d.reserve(n); + for(size_t i = 0, pos = 0; i < n; ++i, pos = ((pos+1)%a.size())) + { + d.push_back(a[pos]); + } + return d; + } +}; + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wmissing-braces" // warning : suggest braces around initialization of subobject [-Wmissing-braces] +#endif + +// for scalar types: ints and floats +template< class T > +struct archetype_proto : public archetype_proto_base< T, archetype_proto<T> > +{ + static_assert(std::is_fundamental< T >::value, "T must be a fundamental type"); + static std::array<T, 8> const& arr() + { + static const std::array<T, 8> arr_{0, 1, 2, 3, 4, 5, 6, 7}; + return arr_; + } + static std::array<Counting<T>, 8> const& carr() + { + static const std::array<Counting<T>, 8> arr_ = {0, 1, 2, 3, 4, 5, 6, 7}; + return arr_; + } + static std::initializer_list< T > il() + { + static const std::initializer_list< T > l{0, 1, 2, 3, 4, 5, 6, 7}; + return l; + } + static std::initializer_list< Counting<T> > cil() + { + static const std::initializer_list< Counting<T> > l = {0, 1, 2, 3, 4, 5, 6, 7}; + C4_ASSERT(l.size() == 8); + return l; + } +}; + +#define _C4_DECLARE_ARCHETYPE_PROTO(ty, ...) \ +template<> \ +struct archetype_proto<ty> : public archetype_proto_base< ty, archetype_proto<ty> > \ +{ \ + static std::array<ty, 8> const& arr() \ + { \ + static const std::array<ty, 8> arr_{__VA_ARGS__}; \ + return arr_; \ + } \ + static std::array<Counting<ty>, 8> const& carr() \ + { \ + static const std::array<Counting<ty>, 8> arr_{__VA_ARGS__}; \ + return arr_; \ + } \ + static std::initializer_list< ty > il() \ + { \ + static const std::initializer_list< ty > l{__VA_ARGS__}; \ + return l; \ + } \ + static std::initializer_list< Counting<ty> > cil() \ + { \ + static const std::initializer_list< Counting<ty> > l{__VA_ARGS__}; \ + return l; \ + } \ +} + +#define _C4_DECLARE_ARCHETYPE_PROTO_TPL1(tplparam1, ty, ...) \ +template< tplparam1 > \ +struct archetype_proto< ty > : public archetype_proto_base< ty, archetype_proto<ty> > \ +{ \ + static std::array<ty, 8> const& arr() \ + { \ + static const std::array<ty, 8> arr_{__VA_ARGS__}; \ + return arr_; \ + } \ + static std::array<Counting<ty>, 8> const& carr() \ + { \ + static const std::array<Counting<ty>, 8> arr_{__VA_ARGS__}; \ + return arr_; \ + } \ + static std::initializer_list< ty > il() \ + { \ + static const std::initializer_list< ty > l{__VA_ARGS__}; \ + return l; \ + } \ + static std::initializer_list< Counting<ty> > cil() \ + { \ + static const std::initializer_list< Counting<ty> > l{__VA_ARGS__}; \ + return l; \ + } \ +} + +_C4_DECLARE_ARCHETYPE_PROTO(std::string, + "str0", "str1", "str2", "str3", + "str4", "str5", "str6", "str7"); + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** Resource-owning archetype */ + +template< class T > +struct exvec3 +{ + T x, y, z; + bool operator== (exvec3 const& that) const + { + return x == that.x && y == that.y && z == that.z; + } +}; +template< class String, class T > +sstream< String >& operator<< (sstream< String >& ss, exvec3<T> const& v) +{ + using char_type = typename sstream< String >::char_type; + ss.printp(C4_TXTTY("({},{},{})", char_type), v.x, v.y, v.z); + return ss; +} +template< class String, class T > +sstream< String >& operator>> (sstream< String >& ss, exvec3<T> & v) +{ + using char_type = typename sstream< String >::char_type; + ss.scanp(C4_TXTTY("({},{},{})", char_type), v.x, v.y, v.z); + return ss; +} + +#define _ C4_COMMA +#define c4v(v0, v1, v2) exvec3<T>{v0 _ v1 _ v2} +_C4_DECLARE_ARCHETYPE_PROTO_TPL1(class T, exvec3<T>, + c4v(0, 1, 2), c4v(3, 4, 5), c4v(6, 7, 8), c4v(9, 10, 11), + c4v(100, 101, 102), c4v(103, 104, 105), c4v(106, 107, 108), c4v(109, 110, 111) + ); +#undef c4v +#undef _ + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** Resource-owning archetype */ +struct IdOwner +{ + static int s_current; + int id; + int val; + + void check() const + { + CHECK_UNARY(id > 0); + } + void check(IdOwner const& that) const + { + check(); + CHECK_NE(id, that.id); + } + + IdOwner(int v = 0) { id = ++s_current; val = v; } + ~IdOwner() { if(id > 0) --s_current; } + IdOwner(IdOwner const& that) { id = ++s_current; val = that.val; } + IdOwner(IdOwner && that) { id = that.id; val = that.val; that.id = 0; } + IdOwner& operator= (IdOwner const& that) { C4_CHECK(id > 0); --s_current; id = ++s_current; val = that.val; return *this; } + IdOwner& operator= (IdOwner && that) { C4_CHECK(id > 0); --s_current; id = that.id; val = that.val; that.id = 0; return *this; } + bool operator== (IdOwner const& that) const + { + return val == that.val; + } +}; + +_C4_DECLARE_ARCHETYPE_PROTO(IdOwner, 0, 1, 2, 3, 4, 5, 6, 7); + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** Memory-owning archetype, raw mem resource calls */ +template< class T > +struct MemOwner +{ + T *mem; + // prevent initialization order problems by using a memory resource here + MemoryResourceMalloc mr; + + void check() const + { + EXPECT_NE(mem, nullptr); + check_archetype(*mem); + } + void check(MemOwner const& that) const + { + EXPECT_NE(mem, that.mem); + } + + ~MemOwner() + { + if(!mem) return; + mem->~T(); + mr.deallocate(mem, sizeof(T), alignof(T)); + mem = nullptr; + } + + MemOwner() + { + mem = (T*)mr.allocate(sizeof(T), alignof(T)); + new (mem) T(); + } + template< class ...Args > + MemOwner(varargs_t, Args && ...args) + { + mem = (T*)mr.allocate(sizeof(T), alignof(T)); + new (mem) T(std::forward< Args >(args)...); + } + MemOwner(MemOwner const& that) + { + mem = (T*)mr.allocate(sizeof(T), alignof(T)); + new (mem) T(*that.mem); + } + MemOwner(MemOwner && that) + { + mem = that.mem; + that.mem = nullptr; + } + MemOwner& operator= (MemOwner const& that) + { + if(!mem) + { + mem = (T*)mr.allocate(sizeof(T), alignof(T)); + } + else + { + mem->~T(); + } + new (mem) T(*that.mem); + return *this; + } + MemOwner& operator= (MemOwner && that) + { + if(mem) + { + mem->~T(); + mr.deallocate(mem, sizeof(T), alignof(T)); + } + mem = that.mem; + that.mem = nullptr; + return *this; + } + bool operator== (MemOwner const& that) const + { + return *mem == *that.mem; + } +}; + +#define _ C4_COMMA +#define c4v(which) MemOwner<T>{varargs _ archetype_proto<T>::get(which)} +_C4_DECLARE_ARCHETYPE_PROTO_TPL1(class T, MemOwner<T>, + c4v(0), c4v(1), c4v(2), c4v(3), + c4v(4), c4v(5), c4v(6), c4v(7) + ); +#undef c4v +#undef _ + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** Memory-owning archetype, with allocator */ +template< class T > +struct MemOwnerAlloc +{ + T *mem; + // prevent initialization order problems by using a memory resource here + MemoryResourceMalloc mr; + c4::Allocator< T > m_alloc; + using alloc_traits = std::allocator_traits< c4::Allocator< T > >; + + void check() const + { + EXPECT_NE(mem, nullptr); + check_archetype(*mem); + } + void check(MemOwnerAlloc const& that) const + { + check(); + EXPECT_NE(mem, that.mem); + } + + void free() + { + alloc_traits::destroy(m_alloc, mem); + alloc_traits::deallocate(m_alloc, mem, 1); + mem = nullptr; + } + ~MemOwnerAlloc() + { + if(!mem) return; + free(); + } + + MemOwnerAlloc() : m_alloc(&mr) + { + C4_ASSERT(m_alloc.resource() == &mr); + mem = alloc_traits::allocate(m_alloc, 1); + alloc_traits::construct(m_alloc, mem); + } + template< class ...Args > + MemOwnerAlloc(varargs_t, Args && ...args) : m_alloc(&mr) + { + mem = alloc_traits::allocate(m_alloc, 1); + alloc_traits::construct(m_alloc, mem, std::forward< Args >(args)...); + } + + MemOwnerAlloc(MemOwnerAlloc const& that) : m_alloc(&mr) + { + mem = alloc_traits::allocate(m_alloc, 1); + alloc_traits::construct(m_alloc, mem, *that.mem); + } + MemOwnerAlloc(MemOwnerAlloc && that) : m_alloc(&mr) + { + mem = that.mem; + that.mem = nullptr; + } + + MemOwnerAlloc& operator= (MemOwnerAlloc const& that) + { + if(!mem) + { + mem = alloc_traits::allocate(m_alloc, 1); + } + else + { + mem->~T(); + } + alloc_traits::construct(m_alloc, mem, *that.mem); + return *this; + } + MemOwnerAlloc& operator= (MemOwnerAlloc && that) + { + if(mem) + { + free(); + } + mem = that.mem; + that.mem = nullptr; + return *this; + } + + bool operator== (MemOwnerAlloc const& that) const + { + return *mem == *that.mem; + } +}; + +#define _ C4_COMMA +#define c4v(which) MemOwnerAlloc<T>{varargs _ archetype_proto<T>::get(which)} +_C4_DECLARE_ARCHETYPE_PROTO_TPL1(class T, MemOwnerAlloc<T>, + c4v(0), c4v(1), c4v(2), c4v(3), + c4v(4), c4v(5), c4v(6), c4v(7) + ); +#undef c4v +#undef _ + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** base class archetype */ +struct Base +{ + virtual ~Base() = default; +protected: + Base() = default; +}; +/** derived class archetype */ +struct Derived : public Base +{ + +}; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template< class T > +struct InsidePtr +{ + T a; + T b; + T c; + T *ptr; + + InsidePtr(int which = 0) : a(), b(), c(), ptr(&a + (which % 3)) {} + InsidePtr(InsidePtr const& that) : a(that.a), b(that.b), c(that.c), ptr(&a + (that.ptr - &that.a)) {} + InsidePtr(InsidePtr && that) : a(std::move(that.a)), b(std::move(that.b)), c(std::move(that.c)), ptr(&a + (that.ptr - &that.a)) { that.ptr = nullptr; } + InsidePtr& operator= (InsidePtr const& that) { a = (that.a); b = (that.b); c = (that.c); ptr = (&a + (that.ptr - &that.a)); return *this; } + InsidePtr& operator= (InsidePtr && that) { a = std::move(that.a); b = std::move(that.b); c = std::move(that.c); ptr = (&a + (that.ptr - &that.a)); that.ptr = nullptr; return *this; } + ~InsidePtr() { EXPECT_TRUE(ptr == &a || ptr == &b || ptr == &c || ptr == nullptr); } + + void check() const + { + EXPECT_TRUE(ptr == &a || ptr == &b || ptr == &c); + } + void check(InsidePtr const& that) const + { + check(); + EXPECT_EQ(ptr - &a, that.ptr - &that.a); + } + bool operator== (InsidePtr const& that) const + { + return that.a == a && that.b == b && that.c == c && (ptr - &a) == (that.ptr - &that.a); + } + +}; + +#define _ C4_COMMA +_C4_DECLARE_ARCHETYPE_PROTO_TPL1(class T, InsidePtr<T>, + 0, 1, 2, 3, 4, 5, 6, 7 + ); +#undef _ + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +# define CALL_FOR_SCALAR_ARCHETYPES(mcr) \ + mcr(int , int) \ + mcr(uint64_t , uint64_t) + +# define CALL_FOR_CONTAINEE_ARCHETYPES(mcr) \ + CALL_FOR_SCALAR_ARCHETYPES(mcr) \ + mcr(MemOwnerAlloc_std_string , archetypes::MemOwnerAlloc<std::string>) + + + +using scalars_quick = std::tuple<int, uint64_t>; +using scalars = std::tuple< + char, wchar_t, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float, double +>; +using containees_quick = std::tuple< + int, + uint64_t, + archetypes::MemOwnerAlloc<std::string> +>; +using containees = std::tuple< + char, wchar_t, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float, double, + archetypes::exvec3<int>, + archetypes::exvec3<float>, + archetypes::IdOwner, + archetypes::MemOwner<int>, + archetypes::MemOwner<std::string>, + archetypes::MemOwnerAlloc<int>, + archetypes::MemOwnerAlloc<std::string>, + archetypes::InsidePtr<int>, + archetypes::InsidePtr<std::string> +>; + + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +} // namespace archetypes +} // namespace c4 + +#endif // _C4_TEST_ARCHETYPES_HPP_ diff --git a/thirdparty/ryml/ext/c4core/test/c4/libtest/supprwarn_pop.hpp b/thirdparty/ryml/ext/c4core/test/c4/libtest/supprwarn_pop.hpp new file mode 100644 index 000000000..695035ef6 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/c4/libtest/supprwarn_pop.hpp @@ -0,0 +1,12 @@ +#ifndef _C4_SUPPRWARN_POP_HPP_ +#define _C4_SUPPRWARN_POP_HPP_ + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif /* SUPPRWARN_POP_H */ diff --git a/thirdparty/ryml/ext/c4core/test/c4/libtest/supprwarn_push.hpp b/thirdparty/ryml/ext/c4core/test/c4/libtest/supprwarn_push.hpp new file mode 100644 index 000000000..4651e227c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/c4/libtest/supprwarn_push.hpp @@ -0,0 +1,48 @@ +#ifndef _C4_LIBTEST_SUPPRWARN_PUSH_HPP_ +#define _C4_LIBTEST_SUPPRWARN_PUSH_HPP_ + +/** @file supprwarn_push.hpp this file contains directives to make the + * compiler ignore warnings in test code. It should NOT be used for c4stl + * itself, but only in test code. */ + +#ifdef __clang__ +# pragma clang diagnostic push + /* NOTE: using , ## __VA_ARGS__ to deal with zero-args calls to + * variadic macros is not portable, but works in clang, gcc, msvc, icc. + * clang requires switching off compiler warnings for pedantic mode. + * @see http://stackoverflow.com/questions/32047685/variadic-macro-without-arguments */ +# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" // warning: token pasting of ',' and __VA_ARGS__ is a GNU extension +# pragma clang diagnostic ignored "-Wunused-local-typedef" +# pragma clang diagnostic ignored "-Wsign-compare" // warning: comparison of integers of different signs: 'const unsigned long' and 'const int' +# pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe +# pragma clang diagnostic ignored "-Wwritable-strings" // ISO C++11 does not allow conversion from string literal to char* +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic ignored "-Wunused-parameter" +#elif defined(__GNUC__) +# pragma GCC diagnostic push + /* GCC also issues a warning for zero-args calls to variadic macros. + * This warning is switched on with -pedantic and apparently there is no + * easy way to turn it off as with clang. But marking this as a system + * header works. + * @see https://gcc.gnu.org/onlinedocs/cpp/System-Headers.html + * @see http://stackoverflow.com/questions/35587137/ */ +# pragma GCC system_header +# pragma GCC diagnostic ignored "-Wvariadic-macros" +# pragma GCC diagnostic ignored "-Wwrite-strings" // ISO C++ forbids converting a string constant to ‘C* {aka char*}’ +# pragma GCC diagnostic ignored "-Wunused-local-typedefs" +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wsign-compare" // warning: comparison of integers of different signs: 'const unsigned long' and 'const int' +# pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe +# pragma GCC diagnostic ignored "-Wpedantic" +# pragma GCC diagnostic ignored "-pedantic" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable:4018) // '>=': signed/unsigned mismatch +# pragma warning(disable:4127) // conditional expression is constant +# pragma warning(disable:4189) // local variable is initialized but not referenced +# pragma warning(disable:4389) // '==': signed/unsigned mismatch +# pragma warning(disable:4702) // unreachable code +#endif + +#endif /* _C4_LIBTEST_SUPPRWARN_PUSH_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/test/c4/libtest/test.cpp b/thirdparty/ryml/ext/c4core/test/c4/libtest/test.cpp new file mode 100644 index 000000000..f005d24ac --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/c4/libtest/test.cpp @@ -0,0 +1,7 @@ +#include "c4/test.hpp" + +namespace c4 { + +size_t TestErrorOccurs::num_errors = 0; + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/test/c4/main.cpp b/thirdparty/ryml/ext/c4core/test/c4/main.cpp new file mode 100644 index 000000000..36c8001e6 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/c4/main.cpp @@ -0,0 +1,3 @@ +#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> diff --git a/thirdparty/ryml/ext/c4core/test/c4/test.hpp b/thirdparty/ryml/ext/c4core/test/c4/test.hpp new file mode 100644 index 000000000..936283a2a --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/c4/test.hpp @@ -0,0 +1,324 @@ +#ifndef _C4_TEST_HPP_ +#define _C4_TEST_HPP_ + +#ifdef C4CORE_SINGLE_HEADER +#include "c4/c4core_all.hpp" +#else +#include "c4/config.hpp" +#include "c4/memory_resource.hpp" +#include "c4/allocator.hpp" +#include "c4/substr.hpp" +#endif +#include <cstdio> +#include <iostream> + +// FIXME - these are just dumb placeholders +#define C4_LOGF_ERR(...) fprintf(stderr, __VA_ARGS__) +#define C4_LOGF_WARN(...) fprintf(stderr, __VA_ARGS__) +#define C4_LOGP(msg, ...) printf(msg) + +#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS +#include <doctest/doctest.h> + +#define CHECK_STREQ(lhs, rhs) CHECK_EQ(c4::to_csubstr(lhs), c4::to_csubstr(rhs)) +#define CHECK_FLOAT_EQ(lhs, rhs) CHECK((double)(lhs) == doctest::Approx((double)(rhs))) + + +namespace c4 { + +template<class C> +inline std::ostream& operator<< (std::ostream& stream, c4::basic_substring<C> s) +{ + stream.write(s.str, std::streamsize(s.len)); + return stream; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** RAII class that tests whether an error occurs inside a scope. */ +struct TestErrorOccurs +{ + TestErrorOccurs(size_t num_expected_errors = 1) + : + expected_errors(num_expected_errors), + tmp_settings(c4::ON_ERROR_CALLBACK, &TestErrorOccurs::error_callback) + { + num_errors = 0; + } + ~TestErrorOccurs() + { + CHECK_EQ(num_errors, expected_errors); + num_errors = 0; + } + + size_t expected_errors; + static size_t num_errors; + ScopedErrorSettings tmp_settings; + static void error_callback(const char* /*msg*/, size_t /*msg_size*/) + { + ++num_errors; + } +}; + +#define C4_EXPECT_ERROR_OCCURS(...) \ + auto _testerroroccurs##__LINE__ = TestErrorOccurs(__VA_ARGS__) + +#if C4_USE_ASSERT +# define C4_EXPECT_ASSERT_TRIGGERS(...) C4_EXPECT_ERROR_OCCURS(__VA_ARGS__) +#else +# define C4_EXPECT_ASSERT_TRIGGERS(...) +#endif + +#if C4_USE_XASSERT +# define C4_EXPECT_XASSERT_TRIGGERS(...) C4_EXPECT_ERROR_OCCURS(__VA_ARGS__) +#else +# define C4_EXPECT_XASSERT_TRIGGERS(...) +#endif + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** count constructors, destructors and assigns */ +template< class T > +struct Counting +{ + using value_type = T; + + T obj; + + bool operator== (T const& that) const { return obj == that; } + + static bool log_ctors; + static size_t num_ctors; + template< class ...Args > + Counting(Args && ...args); + + static bool log_dtors; + static size_t num_dtors; + ~Counting(); + + static bool log_copy_ctors; + static size_t num_copy_ctors; + Counting(Counting const& that); + + static bool log_move_ctors; + static size_t num_move_ctors; + Counting(Counting && that); + + static bool log_copy_assigns; + static size_t num_copy_assigns; + Counting& operator= (Counting const& that); + + static bool log_move_assigns; + static size_t num_move_assigns; + Counting& operator= (Counting && that); + + + struct check_num + { + char const* name; + size_t const& what; + size_t const initial; + size_t const must_be_num; + check_num(char const* nm, size_t const& w, size_t n) : name(nm), what(w), initial(what), must_be_num(n) {} + ~check_num() + { + size_t del = what - initial; + INFO("# of " << name << " calls: expected " << must_be_num << ", but got " << del); + CHECK_EQ(del, must_be_num); + } + }; + + static check_num check_ctors(size_t n) { return check_num("ctor", num_ctors, n); } + static check_num check_dtors(size_t n) { return check_num("dtor", num_dtors, n); } + static check_num check_copy_ctors(size_t n) { return check_num("copy_ctor", num_copy_ctors, n); } + static check_num check_move_ctors(size_t n) { return check_num("move_ctor", num_move_ctors, n); } + static check_num check_copy_assigns(size_t n) { return check_num("copy_assign", num_copy_assigns, n); } + static check_num check_move_assigns(size_t n) { return check_num("move_assign", num_move_assigns, n); } + + struct check_num_ctors_dtors + { + check_num ctors, dtors; + check_num_ctors_dtors(size_t _ctors, size_t _dtors) + : + ctors(check_ctors(_ctors)), + dtors(check_dtors(_dtors)) + { + } + }; + static check_num_ctors_dtors check_ctors_dtors(size_t _ctors, size_t _dtors) + { + return check_num_ctors_dtors(_ctors, _dtors); + } + + struct check_num_all + { + check_num ctors, dtors, cp_ctors, mv_ctors, cp_assigns, mv_assigns; + check_num_all(size_t _ctors, size_t _dtors, size_t _cp_ctors, size_t _mv_ctors, size_t _cp_assigns, size_t _mv_assigns) + { + ctors = check_ctors(_ctors); + dtors = check_dtors(_dtors); + cp_ctors = check_copy_ctors(_cp_ctors); + mv_ctors = check_move_ctors(_mv_ctors); + cp_assigns = check_copy_assigns(_cp_assigns); + mv_assigns = check_move_assigns(_mv_assigns); + } + }; + static check_num_all check_all(size_t _ctors, size_t _dtors, size_t _cp_ctors, size_t _move_ctors, size_t _cp_assigns, size_t _mv_assigns) + { + return check_num_all(_ctors, _dtors, _cp_ctors, _move_ctors, _cp_assigns, _mv_assigns); + } + + static void reset() + { + num_ctors = 0; + num_dtors = 0; + num_copy_ctors = 0; + num_move_ctors = 0; + num_copy_assigns = 0; + num_move_assigns = 0; + } +}; + +template< class T > size_t Counting< T >::num_ctors = 0; +template< class T > bool Counting< T >::log_ctors = false; +template< class T > size_t Counting< T >::num_dtors = 0; +template< class T > bool Counting< T >::log_dtors = false; +template< class T > size_t Counting< T >::num_copy_ctors = 0; +template< class T > bool Counting< T >::log_copy_ctors = false; +template< class T > size_t Counting< T >::num_move_ctors = 0; +template< class T > bool Counting< T >::log_move_ctors = false; +template< class T > size_t Counting< T >::num_copy_assigns = 0; +template< class T > bool Counting< T >::log_copy_assigns = false; +template< class T > size_t Counting< T >::num_move_assigns = 0; +template< class T > bool Counting< T >::log_move_assigns = false; + +template< class T > +template< class ...Args > +Counting< T >::Counting(Args && ...args) : obj(std::forward< Args >(args)...) +{ + if(log_ctors) C4_LOGP("Counting[{}]::ctor #{}\n", (void*)this, num_ctors); + ++num_ctors; +} + +template< class T > +Counting< T >::~Counting() +{ + if(log_dtors) C4_LOGP("Counting[{}]::dtor #{}\n", (void*)this, num_dtors); + ++num_dtors; +} + +template< class T > +Counting< T >::Counting(Counting const& that) : obj(that.obj) +{ + if(log_copy_ctors) C4_LOGP("Counting[{}]::copy_ctor #{}\n", (void*)this, num_copy_ctors); + ++num_copy_ctors; +} + +template< class T > +Counting< T >::Counting(Counting && that) : obj(std::move(that.obj)) +{ + if(log_move_ctors) C4_LOGP("Counting[{}]::move_ctor #{}\n", (void*)this, num_move_ctors); + ++num_move_ctors; +} + +template< class T > +Counting< T >& Counting< T >::operator= (Counting const& that) +{ + obj = that.obj; + if(log_copy_assigns) C4_LOGP("Counting[{}]::copy_assign #{}\n", (void*)this, num_copy_assigns); + ++num_copy_assigns; + return *this; +} + +template< class T > +Counting< T >& Counting< T >::operator= (Counting && that) +{ + obj = std::move(that.obj); + if(log_move_assigns) C4_LOGP("Counting[{}]::move_assign #{}\n", (void*)this, num_move_assigns); + ++num_move_assigns; + return *this; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +/** @todo refactor to use RAII @see Counting */ +struct AllocationCountsChecker : public ScopedMemoryResourceCounts +{ + AllocationCounts first; + +public: + + AllocationCountsChecker() + : + ScopedMemoryResourceCounts(), + first(mr.counts()) + { + } + + AllocationCountsChecker(MemoryResource *mr_) + : + ScopedMemoryResourceCounts(mr_), + first(mr.counts()) + { + } + + void reset() + { + first = mr.counts(); + } + + /** check value of curr allocations and size */ + void check_curr(ssize_t expected_allocs, ssize_t expected_size) const + { + CHECK_EQ(mr.counts().curr.allocs, expected_allocs); + CHECK_EQ(mr.counts().curr.size, expected_size); + } + /** check delta of curr allocations and size since construction or last reset() */ + void check_curr_delta(ssize_t expected_allocs, ssize_t expected_size) const + { + AllocationCounts delta = mr.counts() - first; + CHECK_EQ(delta.curr.allocs, expected_allocs); + CHECK_EQ(delta.curr.size, expected_size); + } + + /** check value of total allocations and size */ + void check_total(ssize_t expected_allocs, ssize_t expected_size) const + { + CHECK_EQ(mr.counts().total.allocs, expected_allocs); + CHECK_EQ(mr.counts().total.size, expected_size); + } + /** check delta of total allocations and size since construction or last reset() */ + void check_total_delta(ssize_t expected_allocs, ssize_t expected_size) const + { + AllocationCounts delta = mr.counts() - first; + CHECK_EQ(delta.total.allocs, expected_allocs); + CHECK_EQ(delta.total.size, expected_size); + } + + /** check value of max allocations and size */ + void check_max(ssize_t expected_max_allocs, ssize_t expected_max_size) const + { + CHECK_EQ(mr.counts().max.allocs, expected_max_allocs); + CHECK_EQ(mr.counts().max.size, expected_max_size); + } + + /** check that since construction or the last reset(): + * - num_allocs occcurred + * - totaling total_size + * - of which the largest is max_size */ + void check_all_delta(ssize_t num_allocs, ssize_t total_size, ssize_t max_size) const + { + check_curr_delta(num_allocs, total_size); + check_total_delta(num_allocs, total_size); + check_max(num_allocs > mr.counts().max.allocs ? num_allocs : mr.counts().max.allocs, + max_size > mr.counts().max.size ? max_size : mr.counts().max.size); + } +}; + +} // namespace c4 + +#endif // _C4_LIBTEST_TEST_HPP_ diff --git a/thirdparty/ryml/ext/c4core/test/printintegers.py b/thirdparty/ryml/ext/c4core/test/printintegers.py new file mode 100644 index 000000000..de7532491 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/printintegers.py @@ -0,0 +1,107 @@ + + +def nb(val): + return [val-1, val, val+1] + + +def ipowers2(min, max): + vals = [] + v = int(min / 2) + while v < -10: + vals += nb(v) + v = int(v / 2) + vals += upowers2(max) + return vals + + +def upowers2(max): + vals = [] + v = 16 + while v < max: + vals += nb(v) + v *= 2 + return vals + + +def ipowers10(min, max): + vals = [] + v = -100 + while v > min: + vals += nb(v) + v *= 10 + vals += upowers10(max) + return vals + + +def upowers10(max): + vals = [] + v = 10 + while v < max: + vals += nb(v) + v *= 10 + return vals + + +def idiv10(min, max): + vals = [] + v = int(min / 10) + while v < -10: + vals.append(v) + v = int(v / 10) + vals += udiv10(max) + return vals + + +def udiv10(max): + vals = [] + v = int(max / 10) + while v > 10: + vals += nb(v) + v = int(v / 10) + return vals + + +def ivals(bits): + min = -(1 << (bits-1)) + max = -min-1 + vals = [min, min+1, min+2, min+3, min+4, min+5] + vals += ipowers2(min, max) + vals += ipowers10(min, max) + vals += idiv10(min, max) + vals += [-11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + vals += [max-5, max-4, max-3, max-2, max-1, max] + return sorted(list(set(vals))) + + +def uvals(bits): + max = (1 << bits) - 1 + vals = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + vals += upowers2(max) + vals += upowers10(max) + vals += udiv10(max) + vals += [max-5, max-4, max-3, max-2, max-1, max] + return sorted(list(set(vals))) + + + +def p(v): + return f' nc({v}, "{v}", "{hex(v)}", "{oct(v)}", "{bin(v)}"),' + + +def pn(numbits, fn): + print() + for a in fn(numbits): + print(p(a)) + + +pn(8, ivals) +pn(8, uvals) + +pn(16, ivals) +pn(16, uvals) + +pn(32, ivals) +pn(32, uvals) + +pn(64, ivals) +pn(64, uvals) diff --git a/thirdparty/ryml/ext/c4core/test/test_allocator.cpp b/thirdparty/ryml/ext/c4core/test/test_allocator.cpp new file mode 100644 index 000000000..78d0daf88 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_allocator.cpp @@ -0,0 +1,246 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/allocator.hpp" +#endif + +#include "c4/test.hpp" +#include "c4/libtest/supprwarn_push.hpp" + +#include <vector> +#include <string> +#include <map> + +namespace c4 { + +template< class T > using small_adapter = c4::small_allocator< T >; +template< class T > using small_adapter_mr = c4::small_allocator_mr< T >; + +#define _c4definealloctypes(Alloc) \ +using AllocInt = typename Alloc::template rebind<int>::other;\ +using AllocChar = typename Alloc::template rebind<char>::other;\ +using _string = std::basic_string< char, std::char_traits<char>, AllocChar >;\ +using AllocString = typename Alloc::template rebind<_string>::other;\ +using AllocPair = typename Alloc::template rebind<std::pair<const _string,int>>::other;\ +using _vector_int = std::vector<int, AllocInt >;\ +using _vector_string = std::vector<_string, AllocString >;\ +using _map_string_int = std::map<_string, int, std::less<_string>, AllocPair >; + +//----------------------------------------------------------------------------- +template< class Alloc > +void test_traits_compat_construct(typename Alloc::value_type const& val, Alloc &a) +{ + using atraits = std::allocator_traits< Alloc >; + using value_type = typename Alloc::value_type; + + value_type *mem = a.allocate(1); + REQUIRE_NE(mem, nullptr); + atraits::construct(a, mem, val); + CHECK_EQ(*mem, val); + + atraits::destroy(a, mem); + a.deallocate(mem, 1); +} + +TEST_CASE("allocator.traits_compat_construct") +{ + allocator<int> a; + test_traits_compat_construct(1, a); +} + +TEST_CASE("small_allocator.traits_compat_construct") +{ + small_allocator<int> a; + test_traits_compat_construct(1, a); +} + +TEST_CASE("allocator_mr_global.traits_compat_construct") +{ + allocator_mr<int> a; + test_traits_compat_construct(1, a); +} + +TEST_CASE("allocator_mr_linear.traits_compat_construct") +{ + MemoryResourceLinear mr(1024); + allocator_mr<int> a(&mr); + test_traits_compat_construct(1, a); +} + +TEST_CASE("allocator_mr_linear_arr.traits_compat_construct") +{ + MemoryResourceLinearArr<1024> mr; + allocator_mr<int> a(&mr); + test_traits_compat_construct(1, a); +} + +TEST_CASE("small_allocator_mr_global.traits_compat_construct") +{ + small_allocator_mr<int> a; + test_traits_compat_construct(1, a); +} + +TEST_CASE("small_allocator_mr_linear.traits_compat_construct") +{ + MemoryResourceLinear mr(1024); + small_allocator_mr<int> a(&mr); + test_traits_compat_construct(1, a); +} + +TEST_CASE("small_allocator_mr_linear_arr.traits_compat_construct") +{ + MemoryResourceLinearArr<1024> mr; + small_allocator_mr<int> a(&mr); + test_traits_compat_construct(1, a); +} + +//----------------------------------------------------------------------------- + +template< class Alloc > +void clear_mr(Alloc a) +{ + auto mrl = dynamic_cast<MemoryResourceLinear*>(a.resource()); + if(mrl) + { + mrl->clear(); + } +} + +template< class Alloc > +void do_std_containers_test(Alloc alloc) +{ + _c4definealloctypes(Alloc); + + { + _string v(alloc); + v.reserve(256); + v = "adskjhsdfkjdflkjsdfkjhsdfkjh"; + CHECK_EQ(v, "adskjhsdfkjdflkjsdfkjhsdfkjh"); + } + + clear_mr(alloc); + + { + int arr[128]; + for(int &i : arr) + { + i = 42; + } + _vector_int vi(arr, arr+C4_COUNTOF(arr), alloc); + for(int i : vi) + { + CHECK_EQ(i, 42); + } + } + + clear_mr(alloc); + + { + AllocChar a = alloc; + _vector_string v({"foo", "bar", "baz", "bat", "bax"}, a); + CHECK_EQ(v.size(), 5); + CHECK_EQ(v[0], "foo"); + CHECK_EQ(v[1], "bar"); + CHECK_EQ(v[2], "baz"); + CHECK_EQ(v[3], "bat"); + CHECK_EQ(v[4], "bax"); + } + + clear_mr(alloc); + + { + AllocString a = alloc; + _vector_string v(a); + v.resize(4); + int count = 0; + for(auto &s : v) + { + _string ss(size_t(64), (char)('0' + count++)); + s = ss; + } + } + + clear_mr(alloc); + + { +#if !defined(__GNUC__) || (__GNUC__ >= 5) + /* constructor does not exist on gcc < 5) */ + AllocPair a = alloc; + _map_string_int v(a); +#else + _map_string_int v; +#endif + CHECK_EQ(v.size(), 0); + v["foo"] = 0; + v["bar"] = 1; + v["baz"] = 2; + v["bat"] = 3; + CHECK_EQ(v.size(), 4); + CHECK_EQ(v["foo"], 0); + CHECK_EQ(v["bar"], 1); + CHECK_EQ(v["baz"], 2); + CHECK_EQ(v["bat"], 3); + } +} + +TEST_CASE("allocator_global.std_containers") +{ + allocator<int> a; + do_std_containers_test(a); +} + +TEST_CASE("small_allocator_global.std_containers") +{ + /* this is failing, investigate + small_allocator<int> a; + do_std_containers_test(a); + */ +} + +TEST_CASE("allocator_mr_global.std_containers") +{ + allocator_mr<int> a; + do_std_containers_test(a); +} + +TEST_CASE("allocator_mr_linear.std_containers") +{ + MemoryResourceLinear mr(1024); + allocator_mr<int> a(&mr); + do_std_containers_test(a); +} + +TEST_CASE("allocator_mr_linear_arr.std_containers") +{ + MemoryResourceLinearArr<1024> mr; + allocator_mr<int> a(&mr); + do_std_containers_test(a); +} + +TEST_CASE("small_allocator_mr_global.std_containers") +{ + /* this is failing, investigate + small_allocator_mr<int> a; + do_std_containers_test(a); + */ +} + +TEST_CASE("small_allocator_mr_linear.std_containers") +{ + /* this is failing, investigate + MemoryResourceLinear mr(1024); + small_allocator_mr<int> a(&mr); + do_std_containers_test(a); + */ +} + +TEST_CASE("small_allocator_mr_linear_arr.std_containers") +{ + /* this is failing, investigate + MemoryResourceLinearArr<1024> mr; + small_allocator_mr<int> a(&mr); + do_std_containers_test(a); + */ +} + +} // namespace c4 + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_base64.cpp b/thirdparty/ryml/ext/c4core/test/test_base64.cpp new file mode 100644 index 000000000..e638a0d81 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_base64.cpp @@ -0,0 +1,265 @@ +#include "c4/test.hpp" +#ifndef C4CORE_SINGLE_HEADER +#include "c4/std/string.hpp" +#include "c4/std/vector.hpp" +#include "c4/format.hpp" +#include "c4/base64.hpp" +#endif + +#include "c4/libtest/supprwarn_push.hpp" + +#include <cstring> + +C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4310) // cast truncates constant value + +namespace c4 { + +namespace detail { +void base64_test_tables(); +TEST_CASE("base64.infrastructure") +{ + #ifndef NDEBUG + detail::base64_test_tables(); + #endif +} +// Since some the macros in c4/cpu.cpp cannot identify endanness at compile +// time, we use a simple runtime endianness-detection routine. +bool is_little_endian() +{ + unsigned long const v = 1UL; + unsigned char b[sizeof(v)]; + std::memcpy(&b[0], &v, sizeof(v)); + return !!b[0]; +} +} // namespace detail + +csubstr native(csubstr little_endian, csubstr big_endian) +{ + return detail::is_little_endian() ? little_endian : big_endian; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T, class U> +void test_base64_str_roundtrip(T const& val, csubstr expected, U *ws) +{ + char buf_[512]; + substr buf(buf_); + + csubstr encoded = to_chars_sub(buf, fmt::base64(val)); + CHECK(base64_valid(encoded)); + CHECK_EQ(encoded, expected); + CHECK_EQ(encoded.len % 4, 0); + + auto req = fmt::base64(*ws); + size_t written = from_chars(encoded, &req); + CHECK_EQ(ws->first(written), val); +} + +template<class T> +void test_base64_roundtrip(T const& val, csubstr expected) +{ + char buf_[512]; + substr buf(buf_); + + csubstr encoded = to_chars_sub(buf, fmt::base64(val)); + CHECK(base64_valid(encoded)); + CHECK_EQ(encoded, expected); + CHECK_EQ(encoded.len % 4, 0); + + T ws = {}; + auto req = fmt::base64(ws); + size_t written = from_chars(encoded, &req); + CHECK_EQ(written, sizeof(T)); + CHECK_EQ(ws, val); +} + +template<class T> +struct base64_test_pair +{ + T val; + csubstr encoded; +}; + +base64_test_pair<csubstr> base64_str_pairs[] = { +#define __(val, expected) {csubstr(val), csubstr(expected)} + __("" , "" ), + __("0" , "MA==" ), + __("1" , "MQ==" ), + __("2" , "Mg==" ), + __("3" , "Mw==" ), + __("4" , "NA==" ), + __("5" , "NQ==" ), + __("6" , "Ng==" ), + __("7" , "Nw==" ), + __("8" , "OA==" ), + __("9" , "OQ==" ), + __("10" , "MTA=" ), + __("123" , "MTIz" ), + __("1234" , "MTIzNA==" ), + __("1235" , "MTIzNQ==" ), + __("Man" , "TWFu" ), + __("Ma" , "TWE=" ), + __("M" , "TQ==" ), + __("deadbeef" , "ZGVhZGJlZWY=" ), + __("any carnal pleasure.", "YW55IGNhcm5hbCBwbGVhc3VyZS4="), + __("any carnal pleasure" , "YW55IGNhcm5hbCBwbGVhc3VyZQ=="), + __("any carnal pleasur" , "YW55IGNhcm5hbCBwbGVhc3Vy" ), + __("any carnal pleasu" , "YW55IGNhcm5hbCBwbGVhc3U=" ), + __("any carnal pleas" , "YW55IGNhcm5hbCBwbGVhcw==" ), + __( "pleasure.", "cGxlYXN1cmUu" ), + __( "leasure.", "bGVhc3VyZS4=" ), + __( "easure.", "ZWFzdXJlLg==" ), + __( "asure.", "YXN1cmUu" ), + __( "sure.", "c3VyZS4=" ), +#undef __ +}; + + +TEST_CASE("base64.str") +{ + char buf_[512]; + substr buf(buf_); + for(auto p : base64_str_pairs) + { + INFO(p.val); + test_base64_str_roundtrip(p.val, p.encoded, &buf); + } +} + +TEST_CASE_TEMPLATE("base64.8bit", T, int8_t, uint8_t) +{ + base64_test_pair<T> pairs[] = { + {(T) 0, csubstr("AA==")}, + {(T) 1, csubstr("AQ==")}, + {(T) 2, csubstr("Ag==")}, + {(T) 3, csubstr("Aw==")}, + {(T) 4, csubstr("BA==")}, + {(T) 5, csubstr("BQ==")}, + {(T) 6, csubstr("Bg==")}, + {(T) 7, csubstr("Bw==")}, + {(T) 8, csubstr("CA==")}, + {(T) 9, csubstr("CQ==")}, + {(T) 10, csubstr("Cg==")}, + {(T) 11, csubstr("Cw==")}, + {(T) 12, csubstr("DA==")}, + {(T) 13, csubstr("DQ==")}, + {(T) 14, csubstr("Dg==")}, + {(T) 15, csubstr("Dw==")}, + {(T) 16, csubstr("EA==")}, + {(T) 17, csubstr("EQ==")}, + {(T) 18, csubstr("Eg==")}, + {(T) 19, csubstr("Ew==")}, + {(T) 20, csubstr("FA==")}, + {(T)127, csubstr("fw==")}, + {(T)128, csubstr("gA==")}, + {(T)254, csubstr("/g==")}, + {(T)255, csubstr("/w==")}, + }; + for(auto p : pairs) + { + INFO("val=" << (int)p.val << " expected=" << p.encoded); + test_base64_roundtrip(p.val, p.encoded); + } +} + +TEST_CASE_TEMPLATE("base64.16bit", T, int16_t, uint16_t) +{ + base64_test_pair<T> pairs[] = { + { 0, native("AAA=", "AAA=")}, + { 1, native("AQA=", "AAE=")}, + { 2, native("AgA=", "AAI=")}, + { 3, native("AwA=", "AAM=")}, + { 4, native("BAA=", "AAQ=")}, + { 5, native("BQA=", "AAU=")}, + { 6, native("BgA=", "AAY=")}, + { 7, native("BwA=", "AAc=")}, + { 8, native("CAA=", "AAg=")}, + { 9, native("CQA=", "AAk=")}, + { 10, native("CgA=", "AAo=")}, + {1234, native("0gQ=", "BNI=")}, + }; + for(auto p : pairs) + { + INFO("val=" << p.val << " expected=" << p.encoded); + test_base64_roundtrip(p.val, p.encoded); + } +} + +TEST_CASE_TEMPLATE("base64.32bit", T, int32_t, uint32_t) +{ + base64_test_pair<T> pairs[] = { + { 0, native("AAAAAA==", "AAAAAA==")}, + { 1, native("AQAAAA==", "AAAAAQ==")}, + { 2, native("AgAAAA==", "AAAAAg==")}, + { 3, native("AwAAAA==", "AAAAAw==")}, + { 4, native("BAAAAA==", "AAAABA==")}, + { 5, native("BQAAAA==", "AAAABQ==")}, + { 6, native("BgAAAA==", "AAAABg==")}, + { 7, native("BwAAAA==", "AAAABw==")}, + { 8, native("CAAAAA==", "AAAACA==")}, + { 9, native("CQAAAA==", "AAAACQ==")}, + { 10, native("CgAAAA==", "AAAACg==")}, + {1234, native("0gQAAA==", "AAAE0g==")}, + }; + for(auto p : pairs) + { + INFO("val=" << p.val << " expected=" << p.encoded); + test_base64_roundtrip(p.val, p.encoded); + } +} + +TEST_CASE_TEMPLATE("base64.64bit", T, int64_t, uint64_t) +{ + base64_test_pair<T> pairs[] = { + { 0, native("AAAAAAAAAAA=", "AAAAAAAAAAA=")}, + { 1, native("AQAAAAAAAAA=", "AAAAAAAAAAE=")}, + { 2, native("AgAAAAAAAAA=", "AAAAAAAAAAI=")}, + { 3, native("AwAAAAAAAAA=", "AAAAAAAAAAM=")}, + { 4, native("BAAAAAAAAAA=", "AAAAAAAAAAQ=")}, + { 5, native("BQAAAAAAAAA=", "AAAAAAAAAAU=")}, + { 6, native("BgAAAAAAAAA=", "AAAAAAAAAAY=")}, + { 7, native("BwAAAAAAAAA=", "AAAAAAAAAAc=")}, + { 8, native("CAAAAAAAAAA=", "AAAAAAAAAAg=")}, + { 9, native("CQAAAAAAAAA=", "AAAAAAAAAAk=")}, + { 10, native("CgAAAAAAAAA=", "AAAAAAAAAAo=")}, + {1234, native("0gQAAAAAAAA=", "AAAAAAAABNI=")}, + {0xdeadbeef, native("776t3gAAAAA=", "AAAAAN6tvu8=")}, + }; + for(auto p : pairs) + { + INFO("val=" << p.val << " expected=" << p.encoded); + test_base64_roundtrip(p.val, p.encoded); + } +} + +TEST_CASE("base64.high_bits_u32") +{ + test_base64_roundtrip(UINT32_C(0xdeadbeef), native("776t3g==", "3q2+7w==")); + test_base64_roundtrip(UINT32_MAX, native("/////w==", "/////w==")); +} + +TEST_CASE("base64.high_bits_i32") +{ + test_base64_roundtrip(INT32_C(0x7fffffff), native("////fw==", "f////w==")); + test_base64_roundtrip(INT32_MAX, native("////fw==", "f////w==")); +} + +TEST_CASE("base64.high_bits_u64") +{ + test_base64_roundtrip(UINT64_MAX, native("//////////8=", "//////////8=")); +} + +TEST_CASE("base64.high_bits_i64") +{ + test_base64_roundtrip(INT64_MAX, native("/////////38=", "f/////////8=")); +} + +} // namespace c4 + +C4_SUPPRESS_WARNING_MSVC_POP + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_bitmask.cpp b/thirdparty/ryml/ext/c4core/test/test_bitmask.cpp new file mode 100644 index 000000000..e7137d2a5 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_bitmask.cpp @@ -0,0 +1,385 @@ +#include <vector> +#include <sstream> + +#ifndef C4CORE_SINGLE_HEADER +#include <c4/bitmask.hpp> +#include <c4/std/vector.hpp> +#endif + +#include <c4/test.hpp> + +#include "./test_enum_common.hpp" + +template<typename Enum> +void cmp_enum(Enum lhs, Enum rhs) +{ + using I = typename std::underlying_type<Enum>::type; + CHECK_EQ(static_cast<I>(lhs), static_cast<I>(rhs)); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +TEST_CASE("str2bm.simple_bitmask") +{ + using namespace c4; + std::vector<char> str; + + CHECK_EQ(BM_NONE, str2bm<MyBitmask>("BM_NONE")); + CHECK_EQ(BM_NONE, str2bm<MyBitmask>("NONE")); + CHECK_EQ(BM_NONE, str2bm<MyBitmask>("0")); + CHECK_EQ(BM_NONE, str2bm<MyBitmask>("0x0")); + + CHECK_EQ(BM_FOO, str2bm<MyBitmask>("BM_FOO")); + CHECK_EQ(BM_FOO, str2bm<MyBitmask>("FOO")); + CHECK_EQ(BM_FOO, str2bm<MyBitmask>("1")); + CHECK_EQ(BM_FOO, str2bm<MyBitmask>("0x1")); + CHECK_EQ(BM_FOO, str2bm<MyBitmask>("BM_NONE|0x1")); + + CHECK_EQ(BM_BAR, str2bm<MyBitmask>("BM_BAR")); + CHECK_EQ(BM_BAR, str2bm<MyBitmask>("BAR")); + CHECK_EQ(BM_BAR, str2bm<MyBitmask>("2")); + CHECK_EQ(BM_BAR, str2bm<MyBitmask>("0x2")); + CHECK_EQ(BM_BAR, str2bm<MyBitmask>("BM_NONE|0x2")); + + CHECK_EQ(BM_BAZ, str2bm<MyBitmask>("BM_BAZ")); + CHECK_EQ(BM_BAZ, str2bm<MyBitmask>("BAZ")); + CHECK_EQ(BM_BAZ, str2bm<MyBitmask>("4")); + CHECK_EQ(BM_BAZ, str2bm<MyBitmask>("0x4")); + + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("BM_FOO|BM_BAR")); + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("BM_FOO|BAR")); + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("FOO|BM_BAR")); + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("BM_FOO_BAR")); + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("FOO_BAR")); + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("FOO|BAR")); + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("0x1|0x2")); + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("1|2")); + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("0x3")); + CHECK_EQ(BM_FOO_BAR, str2bm<MyBitmask>("3")); + + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("BM_FOO|BM_BAR|BM_BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("BM_FOO|BM_BAR|BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("BM_FOO|BAR|BM_BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("FOO|BM_BAR|BM_BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("FOO|BM_BAR|BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("FOO|BAR|BM_BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("FOO|BAR|BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("FOO_BAR|BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("BM_FOO_BAR|BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("0x1|BAR|BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("FOO|0x2|BAZ")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("FOO|BAR|0x4")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("0x1|0x2|0x4")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("0x7")); + CHECK_EQ(BM_FOO_BAR_BAZ, str2bm<MyBitmask>("7")); +} + +TEST_CASE("str2bm.scoped_bitmask") +{ + using namespace c4; + std::vector<char> str; + using bmt = MyBitmaskClass; + + cmp_enum(bmt::BM_NONE, (bmt)str2bm<bmt>("MyBitmaskClass::BM_NONE")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("MyBitmaskClass::BM_BAR")); + cmp_enum(bmt::BM_BAZ, (bmt)str2bm<bmt>("MyBitmaskClass::BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO_BAR")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO_BAR_BAZ")); + cmp_enum(bmt::BM_NONE, (bmt)str2bm<bmt>("BM_NONE")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("BM_FOO")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("BM_BAR")); + cmp_enum(bmt::BM_BAZ, (bmt)str2bm<bmt>("BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("BM_FOO_BAR")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("BM_FOO_BAR_BAZ")); + cmp_enum(bmt::BM_NONE, (bmt)str2bm<bmt>("NONE")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("FOO")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("BAR")); + cmp_enum(bmt::BM_BAZ, (bmt)str2bm<bmt>("BAZ")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("FOO_BAR")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO_BAR_BAZ")); + + cmp_enum(bmt::BM_NONE, (bmt)str2bm<bmt>("NONE")); + cmp_enum(bmt::BM_NONE, (bmt)str2bm<bmt>("BM_NONE")); + cmp_enum(bmt::BM_NONE, (bmt)str2bm<bmt>("MyBitmaskClass::BM_NONE")); + cmp_enum(bmt::BM_NONE, (bmt)str2bm<bmt>("0")); + cmp_enum(bmt::BM_NONE, (bmt)str2bm<bmt>("0x0")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("FOO")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("BM_FOO")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("1")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("0x1")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("NONE|0x1")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("BM_NONE|0x1")); + cmp_enum(bmt::BM_FOO, (bmt)str2bm<bmt>("MyBitmaskClass::BM_NONE|0x1")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("BAR")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("BM_BAR")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("MyBitmaskClass::BM_BAR")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("2")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("0x2")); + cmp_enum(bmt::BM_BAZ, (bmt)str2bm<bmt>("BAZ")); + cmp_enum(bmt::BM_BAZ, (bmt)str2bm<bmt>("BM_BAZ")); + cmp_enum(bmt::BM_BAZ, (bmt)str2bm<bmt>("MyBitmaskClass::BM_BAZ")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("BM_NONE|0x2")); + cmp_enum(bmt::BM_BAR, (bmt)str2bm<bmt>("MyBitmaskClass::BM_NONE|0x2")); + cmp_enum(bmt::BM_BAZ, (bmt)str2bm<bmt>("4")); + cmp_enum(bmt::BM_BAZ, (bmt)str2bm<bmt>("0x4")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("BM_FOO|BM_BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO|MyBitmaskClass::BM_BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("BM_FOO|BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO|BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("FOO|BM_BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("FOO|MyBitmaskClass::BM_BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("BM_FOO_BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO_BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("FOO_BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("FOO|BAR")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("0x1|0x2")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("1|2")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("0x3")); + cmp_enum(bmt::BM_FOO_BAR, (bmt)str2bm<bmt>("3")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("BM_FOO|BM_BAR|BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO|MyBitmaskClass::BM_BAR|MyBitmaskClass::BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("BM_FOO|BM_BAR|BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO|MyBitmaskClass::BM_BAR|BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("BM_FOO|BAR|BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("BM_FOO|BAR|MyBitmaskClass::BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO|BM_BAR|BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO|MyBitmaskClass::BM_BAR|MyBitmaskClass::BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO|BM_BAR|BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO|MyBitmaskClass::BM_BAR|BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO|BAR|BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO|BAR|MyBitmaskClass::BM_BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO|BAR|BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO_BAR|BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("MyBitmaskClass::BM_FOO_BAR|BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("0x1|BAR|BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO|0x2|BAZ")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("FOO|BAR|0x4")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("0x1|0x2|0x4")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("0x7")); + cmp_enum(bmt::BM_FOO_BAR_BAZ, (bmt)str2bm<bmt>("0x7")); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +template<class Enum> +const char* do_bm2str(Enum e, std::vector<char> *s, c4::EnumOffsetType which) +{ + size_t len = c4::bm2str<Enum>(e, nullptr, 0, which); + C4_CHECK(len > 0); + s->resize(len); + C4_CHECK(s->data() != nullptr); + c4::bm2str<Enum>(e, s->data(), s->size(), which); + return s->data(); +} + +TEST_CASE("bm2str.simple_bitmask") +{ + using namespace c4; + std::vector<char> str; + + CHECK_STREQ(do_bm2str(BM_NONE, &str, EOFFS_NONE), "BM_NONE"); + CHECK_STREQ(do_bm2str(BM_FOO, &str, EOFFS_NONE), "BM_FOO"); + CHECK_STREQ(do_bm2str(BM_BAR, &str, EOFFS_NONE), "BM_BAR"); + CHECK_STREQ(do_bm2str(BM_BAZ, &str, EOFFS_NONE), "BM_BAZ"); + CHECK_STREQ(do_bm2str(BM_FOO_BAR, &str, EOFFS_NONE), "BM_FOO_BAR"); + CHECK_STREQ(do_bm2str(BM_FOO_BAR_BAZ, &str, EOFFS_NONE), "BM_FOO_BAR_BAZ"); + CHECK_STREQ(do_bm2str(BM_NONE, &str, EOFFS_CLS ), "BM_NONE"); + CHECK_STREQ(do_bm2str(BM_FOO, &str, EOFFS_CLS ), "BM_FOO"); + CHECK_STREQ(do_bm2str(BM_BAR, &str, EOFFS_CLS ), "BM_BAR"); + CHECK_STREQ(do_bm2str(BM_BAZ, &str, EOFFS_CLS ), "BM_BAZ"); + CHECK_STREQ(do_bm2str(BM_FOO_BAR, &str, EOFFS_CLS ), "BM_FOO_BAR"); + CHECK_STREQ(do_bm2str(BM_FOO_BAR_BAZ, &str, EOFFS_CLS ), "BM_FOO_BAR_BAZ"); + CHECK_STREQ(do_bm2str(BM_NONE, &str, EOFFS_PFX ), "NONE"); + CHECK_STREQ(do_bm2str(BM_FOO, &str, EOFFS_PFX ), "FOO"); + CHECK_STREQ(do_bm2str(BM_BAR, &str, EOFFS_PFX ), "BAR"); + CHECK_STREQ(do_bm2str(BM_BAZ, &str, EOFFS_PFX ), "BAZ"); + CHECK_STREQ(do_bm2str(BM_FOO_BAR, &str, EOFFS_PFX ), "FOO_BAR"); + CHECK_STREQ(do_bm2str(BM_FOO_BAR_BAZ, &str, EOFFS_PFX ), "FOO_BAR_BAZ"); +} + +TEST_CASE("bm2str.scoped_bitmask") +{ + using namespace c4; + std::vector<char> str; + + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_NONE, &str, EOFFS_NONE), "MyBitmaskClass::BM_NONE"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_FOO, &str, EOFFS_NONE), "MyBitmaskClass::BM_FOO"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_BAR, &str, EOFFS_NONE), "MyBitmaskClass::BM_BAR"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_BAZ, &str, EOFFS_NONE), "MyBitmaskClass::BM_BAZ"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_FOO_BAR, &str, EOFFS_NONE), "MyBitmaskClass::BM_FOO_BAR"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_FOO_BAR_BAZ, &str, EOFFS_NONE), "MyBitmaskClass::BM_FOO_BAR_BAZ"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_NONE, &str, EOFFS_CLS ), "BM_NONE"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_FOO, &str, EOFFS_CLS ), "BM_FOO"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_BAR, &str, EOFFS_CLS ), "BM_BAR"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_BAZ, &str, EOFFS_CLS ), "BM_BAZ"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_FOO_BAR, &str, EOFFS_CLS ), "BM_FOO_BAR"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_FOO_BAR_BAZ, &str, EOFFS_CLS ), "BM_FOO_BAR_BAZ"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_NONE, &str, EOFFS_PFX ), "NONE"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_FOO, &str, EOFFS_PFX ), "FOO"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_BAR, &str, EOFFS_PFX ), "BAR"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_BAZ, &str, EOFFS_PFX ), "BAZ"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_FOO_BAR, &str, EOFFS_PFX ), "FOO_BAR"); + CHECK_STREQ(do_bm2str(MyBitmaskClass::BM_FOO_BAR_BAZ, &str, EOFFS_PFX ), "FOO_BAR_BAZ"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +template<class Enum> +std::string do_bm2stream(Enum e, c4::EnumOffsetType which) +{ + std::stringstream ss; + c4::bm2stream<Enum>(ss, e, which); + return ss.str(); +} + +TEST_CASE("bm2stream.simple_bitmask") +{ + using namespace c4; + + CHECK_EQ(do_bm2stream(BM_NONE, EOFFS_NONE), "BM_NONE"); + CHECK_EQ(do_bm2stream(BM_FOO, EOFFS_NONE), "BM_FOO"); + CHECK_EQ(do_bm2stream(BM_BAR, EOFFS_NONE), "BM_BAR"); + CHECK_EQ(do_bm2stream(BM_BAZ, EOFFS_NONE), "BM_BAZ"); + CHECK_EQ(do_bm2stream(BM_FOO_BAR, EOFFS_NONE), "BM_FOO_BAR"); + CHECK_EQ(do_bm2stream(BM_FOO_BAR_BAZ, EOFFS_NONE), "BM_FOO_BAR_BAZ"); + CHECK_EQ(do_bm2stream(BM_NONE, EOFFS_CLS ), "BM_NONE"); + CHECK_EQ(do_bm2stream(BM_FOO, EOFFS_CLS ), "BM_FOO"); + CHECK_EQ(do_bm2stream(BM_BAR, EOFFS_CLS ), "BM_BAR"); + CHECK_EQ(do_bm2stream(BM_BAZ, EOFFS_CLS ), "BM_BAZ"); + CHECK_EQ(do_bm2stream(BM_FOO_BAR, EOFFS_CLS ), "BM_FOO_BAR"); + CHECK_EQ(do_bm2stream(BM_FOO_BAR_BAZ, EOFFS_CLS ), "BM_FOO_BAR_BAZ"); + CHECK_EQ(do_bm2stream(BM_NONE, EOFFS_PFX ), "NONE"); + CHECK_EQ(do_bm2stream(BM_FOO, EOFFS_PFX ), "FOO"); + CHECK_EQ(do_bm2stream(BM_BAR, EOFFS_PFX ), "BAR"); + CHECK_EQ(do_bm2stream(BM_BAZ, EOFFS_PFX ), "BAZ"); + CHECK_EQ(do_bm2stream(BM_FOO_BAR, EOFFS_PFX ), "FOO_BAR"); + CHECK_EQ(do_bm2stream(BM_FOO_BAR_BAZ, EOFFS_PFX ), "FOO_BAR_BAZ"); +} + +TEST_CASE("bm2stream.scoped_bitmask") +{ + using namespace c4; + + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_NONE, EOFFS_NONE), "MyBitmaskClass::BM_NONE"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_FOO, EOFFS_NONE), "MyBitmaskClass::BM_FOO"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_BAR, EOFFS_NONE), "MyBitmaskClass::BM_BAR"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_BAZ, EOFFS_NONE), "MyBitmaskClass::BM_BAZ"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_FOO_BAR, EOFFS_NONE), "MyBitmaskClass::BM_FOO_BAR"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_FOO_BAR_BAZ, EOFFS_NONE), "MyBitmaskClass::BM_FOO_BAR_BAZ"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_NONE, EOFFS_CLS ), "BM_NONE"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_FOO, EOFFS_CLS ), "BM_FOO"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_BAR, EOFFS_CLS ), "BM_BAR"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_BAZ, EOFFS_CLS ), "BM_BAZ"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_FOO_BAR, EOFFS_CLS ), "BM_FOO_BAR"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_FOO_BAR_BAZ, EOFFS_CLS ), "BM_FOO_BAR_BAZ"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_NONE, EOFFS_PFX ), "NONE"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_FOO, EOFFS_PFX ), "FOO"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_BAR, EOFFS_PFX ), "BAR"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_BAZ, EOFFS_PFX ), "BAZ"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_FOO_BAR, EOFFS_PFX ), "FOO_BAR"); + CHECK_EQ(do_bm2stream(MyBitmaskClass::BM_FOO_BAR_BAZ, EOFFS_PFX ), "FOO_BAR_BAZ"); +} + +TEST_CASE("bm2stream.simple_bitmask_without_null_symbol") +{ + using namespace c4; + + CHECK_EQ(do_bm2stream(BM_KABOOM, EOFFS_NONE), "KABOOM"); + CHECK_EQ(do_bm2stream<BmWithoutNull>((BmWithoutNull)0, EOFFS_NONE), "0"); +} + +TEST_CASE("bm2stream.bitmask_class_without_null_symbol") +{ + using namespace c4; + + CHECK_EQ(do_bm2stream(BmClassWithoutNull::BM_KABOOM, EOFFS_PFX), "KABOOM"); + CHECK_EQ(do_bm2stream<BmClassWithoutNull>((BmClassWithoutNull)0, EOFFS_NONE), "0"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +// TODO +template<typename Enum> +void test_bm2str() +{ + using namespace c4; + using I = typename std::underlying_type<Enum>::type; + int combination_depth = 4; + auto syms = esyms<Enum>(); + + std::vector<int> indices; + std::string str; + std::vector<char> ws; + I val = 0, res; + size_t len; + + for(int k = 1; k <= combination_depth; ++k) + { + indices.clear(); + indices.resize(static_cast<size_t>(k)); + while(1) + { + str.clear(); + val = 0; + for(auto i : indices) + { + if(!str.empty()) str += '|'; + str += syms[i].name; + val |= static_cast<I>(syms[i].value); + //printf("%d", i); + } + //len = bm2str<Enum>(val); // needed length + //ws.resize(len); + //bm2str<Enum>(val, &ws[0], len); + //printf(": %s (%zu) %s\n", str.c_str(), (uint64_t)val, ws.data()); + + res = str2bm<Enum>(str.data()); + CHECK_EQ(res, val); + + len = bm2str<Enum>(res); // needed length + ws.resize(len); + bm2str<Enum>(val, &ws[0], len); + res = str2bm<Enum>(ws.data()); + CHECK_EQ(res, val); + + // write a string with the bitmask as an int + c4::catrs(&ws, val); + res = str2bm<Enum>(str.data()); + CHECK_EQ(res, val); + + bool carry = true; + for(size_t i = static_cast<size_t>(k-1); i != size_t(-1); --i) + { + if(indices[i] + 1 < syms.size()) + { + ++indices[i]; + carry = false; + break; + } + else + { + indices[i] = 0; + } + } + if(carry) + { + break; + } + } // while(1) + } // for k +} diff --git a/thirdparty/ryml/ext/c4core/test/test_blob.cpp b/thirdparty/ryml/ext/c4core/test/test_blob.cpp new file mode 100644 index 000000000..b803ba864 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_blob.cpp @@ -0,0 +1,43 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/blob.hpp" +#endif + +#include "c4/test.hpp" + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcast-align" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif + + +namespace c4 { + +template<class T> +void test_blob() +{ + T v; + blob b(v); + CHECK_EQ((T*)b.buf, &v); + CHECK_EQ(b.len, sizeof(T)); + + blob b2 = b; + CHECK_EQ((T*)b2.buf, &v); + CHECK_EQ(b2.len, sizeof(T)); +} + +TEST_CASE("blob.basic") +{ + test_blob<int>(); +} + + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/test/test_char_traits.cpp b/thirdparty/ryml/ext/c4core/test/test_char_traits.cpp new file mode 100644 index 000000000..0f4e01518 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_char_traits.cpp @@ -0,0 +1,67 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/char_traits.hpp" +#endif + +#include "c4/test.hpp" + +namespace c4 { + +TEST_CASE("num_needed_chars.char") +{ + CHECK_EQ(num_needed_chars<char>(0), 0); + CHECK_EQ(num_needed_chars<char>(1), 1); + CHECK_EQ(num_needed_chars<char>(2), 2); + CHECK_EQ(num_needed_chars<char>(3), 3); + CHECK_EQ(num_needed_chars<char>(4), 4); + for(int i = 0; i < 100; ++i) + { + CHECK_EQ(num_needed_chars<char>(i), i); + } +} + +TEST_CASE("num_needed_chars.wchar_t") +{ +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) // C4127: conditional expression is constant +#endif + if(sizeof(wchar_t) == 2) + { + CHECK_EQ(num_needed_chars<wchar_t>( 0), 0); + CHECK_EQ(num_needed_chars<wchar_t>( 1), 1); + CHECK_EQ(num_needed_chars<wchar_t>( 2), 1); + CHECK_EQ(num_needed_chars<wchar_t>( 3), 2); + CHECK_EQ(num_needed_chars<wchar_t>( 4), 2); + CHECK_EQ(num_needed_chars<wchar_t>( 97), 49); + CHECK_EQ(num_needed_chars<wchar_t>( 98), 49); + CHECK_EQ(num_needed_chars<wchar_t>( 99), 50); + CHECK_EQ(num_needed_chars<wchar_t>(100), 50); + CHECK_EQ(num_needed_chars<wchar_t>(101), 51); + } + else if(sizeof(wchar_t) == 4) + { + CHECK_EQ(num_needed_chars<wchar_t>( 0), 0); + CHECK_EQ(num_needed_chars<wchar_t>( 1), 1); + CHECK_EQ(num_needed_chars<wchar_t>( 2), 1); + CHECK_EQ(num_needed_chars<wchar_t>( 3), 1); + CHECK_EQ(num_needed_chars<wchar_t>( 4), 1); + CHECK_EQ(num_needed_chars<wchar_t>( 5), 2); + CHECK_EQ(num_needed_chars<wchar_t>( 6), 2); + CHECK_EQ(num_needed_chars<wchar_t>( 7), 2); + CHECK_EQ(num_needed_chars<wchar_t>( 8), 2); + CHECK_EQ(num_needed_chars<wchar_t>( 93), 24); + CHECK_EQ(num_needed_chars<wchar_t>( 94), 24); + CHECK_EQ(num_needed_chars<wchar_t>( 95), 24); + CHECK_EQ(num_needed_chars<wchar_t>( 96), 24); + CHECK_EQ(num_needed_chars<wchar_t>( 97), 25); + CHECK_EQ(num_needed_chars<wchar_t>( 98), 25); + CHECK_EQ(num_needed_chars<wchar_t>( 99), 25); + CHECK_EQ(num_needed_chars<wchar_t>(100), 25); + CHECK_EQ(num_needed_chars<wchar_t>(101), 26); + } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +} + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/test/test_charconv.cpp b/thirdparty/ryml/ext/c4core/test/test_charconv.cpp new file mode 100644 index 000000000..baa840d1e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_charconv.cpp @@ -0,0 +1,2743 @@ +#ifdef C4CORE_SINGLE_HEADER +#include "c4/c4core_all.hpp" +#else +#include "c4/std/std.hpp" +#include "c4/charconv.hpp" +#include "c4/format.hpp" +#include "c4/type_name.hpp" +#endif + +#include "c4/libtest/supprwarn_push.hpp" + +C4_SUPPRESS_WARNING_GCC_PUSH +C4_SUPPRESS_WARNING_GCC("-Wfloat-equal") +C4_SUPPRESS_WARNING_GCC("-Wuseless-cast") +C4_SUPPRESS_WARNING_GCC("-Wconversion") +C4_SUPPRESS_WARNING_GCC("-Wtype-limits") +C4_SUPPRESS_WARNING_GCC("-Wfloat-equal") +#if defined (__GNUC__) && __GNUC_MAJOR__ >= 7 +C4_SUPPRESS_WARNING_GCC("-Wno-noexcept-type") +#endif +C4_SUPPRESS_WARNING_CLANG_PUSH +C4_SUPPRESS_WARNING_CLANG("-Wfloat-equal") + +#include <c4/test.hpp> + +#include "./test_numbers.hpp" + +namespace c4 { + +namespace { + +// skip the radix prefix: 0x, -0x, 0X, -0X, 0b, -0B, etc +csubstr nopfx(csubstr num) +{ + if(num.begins_with('-')) + num = num.sub(1); + if(num.len >= 2 && num[0] == '0') + { + switch(num[1]) + { + case 'x': case 'X': + case 'o': case 'O': + case 'b': case 'B': + num = num.sub(2); + } + } + return num; +} + +// filter out the radix prefix from anywhere: 0x, -0x, 0X, -0X, 0b, -0B, etc +csubstr nopfx(substr buf, csubstr num) +{ + REQUIRE_GE(buf.len, num.len); + if(num.begins_with('-')) + num = num.sub(1); + size_t pos = 0; + for(size_t i = 0; i < num.len; ++i) + { + const char c = num.str[i]; + if(c == '0') + { + const char n = i+1 < num.len ? num.str[i+1] : '\0'; + switch(n) + { + case 'x': case 'X': + case 'o': case 'O': + case 'b': case 'B': + ++i; + break; + default: + buf[pos++] = c; + break; + } + } + else + { + buf[pos++] = c; + } + } + return buf.first(pos); +} + +// capitalize the alphabetical characters +// eg 0xdeadbeef --> 0XDEADBEEF +substr capitalize(substr buf, csubstr str) +{ + C4_ASSERT(!buf.overlaps(str)); + memcpy(buf.str, str.str, str.len); + substr ret = buf.first(str.len); + ret.toupper(); + return ret; +} + +// prepend zeroes to the left of the number: +// eg 1234 --> 00001234 +// eg -1234 --> -00001234 +// eg 0x1234 --> 0x00001234 +// eg -0x1234 --> -0x00001234 +substr zpad(substr buf, csubstr str, size_t num_zeroes) +{ + C4_ASSERT(!buf.overlaps(str)); + size_t pos = 0; + if(str.len > 0 && str[0] == '-') + buf.str[pos++] = '-'; + if(str.len >= pos+2 && str[pos] == '0') + { + switch(str[pos+1]) + { + case 'x': case 'X': + case 'o': case 'O': + case 'b': case 'B': + memcpy(buf.str + pos, str.str + pos, 2); + pos += 2; + } + } + memset(buf.str + pos, '0', num_zeroes); + csubstr rem = str.sub(pos); + memcpy(buf.str + pos + num_zeroes, rem.str, rem.len); + return buf.first(str.len + num_zeroes); +} + +// get the front element of the type's test numbers +template<class T> +number_case<T> const& front(size_t skip=0) +{ + return *(numbers<T>::vals + skip); +} + +// get the back element of the type's test numbers +template<class T> +number_case<T> const& back(size_t skip=0) +{ + return *(numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 1 - skip); +} + +// given an element, get the n-th element previous to that +template<class T> +number_case<T> const& prev(number_case<T> const& curr, size_t less=1) +{ + C4_ASSERT(less >= 0); + size_t num = C4_COUNTOF(numbers<T>::vals); + C4_ASSERT(&curr >= numbers<T>::vals); + C4_ASSERT(&curr < numbers<T>::vals + num); + size_t icurr = (size_t)(&curr - numbers<T>::vals); + size_t prev = (icurr + num - less) % num; + return *(numbers<T>::vals + prev); +} + +// given an element, get the n-th element after that +template<class T> +number_case<T> const& next(number_case<T> const& curr, size_t more=1) +{ + C4_ASSERT(more >= 0); + size_t num = C4_COUNTOF(numbers<T>::vals); + C4_ASSERT(&curr >= numbers<T>::vals); + C4_ASSERT(&curr < numbers<T>::vals + num); + size_t icurr = (size_t)(&curr - numbers<T>::vals); + size_t next = (icurr + more) % num; + return *(numbers<T>::vals + next); +} + +// construct a string of a value such that it overflows an original value by a given amount +template<class T> +csubstr overflow_by(substr buf, T val, T how_much, T radix) +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_STATIC_ASSERT(sizeof(T) < sizeof(int64_t)); + using upcast_t = typename std::conditional<std::is_signed<T>::value, int64_t, uint64_t>::type; + upcast_t uval = (upcast_t) val; + uval += (upcast_t) how_much; + size_t len = xtoa(buf, uval, (upcast_t)radix); + REQUIRE_GE(buf.len, len); + return buf.first(len); +} + +// construct a string of a value such that it underflows an original value by a given amount +template<class T> +csubstr underflow_by(substr buf, T val, T how_much, T radix) +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + C4_STATIC_ASSERT(sizeof(T) < sizeof(int64_t)); + using upcast_t = typename std::conditional<std::is_signed<T>::value, int64_t, uint64_t>::type; + upcast_t uval = (upcast_t) val; + uval -= (upcast_t) how_much; + size_t len = xtoa(buf, uval, (upcast_t)radix); + REQUIRE_GE(buf.len, len); + return buf.first(len); +} + +} // namespace + +TEST_CASE("charconv.to_chars_format") +{ +#if C4CORE_HAVE_STD_TO_CHARS + CHECK(FTOA_FLOAT == static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::fixed)); + CHECK(FTOA_SCIENT == static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::scientific)); + CHECK(FTOA_FLEX == static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::general)); + CHECK(FTOA_HEXA == static_cast<std::underlying_type<std::chars_format>::type>(std::chars_format::hex)); +#elif !C4CORE_HAVE_FAST_FLOAT + CHECK(FTOA_FLOAT == 'f'); + CHECK(FTOA_SCIENT == 'e'); + CHECK(FTOA_FLEX == 'g'); + CHECK(FTOA_HEXA == 'a'); +#endif +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("test_util.number_cases", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + CHECK_GT(number.dec.len, 0); + CHECK_GT(number.hex.len, 2); + CHECK_GT(number.oct.len, 2); + CHECK_GT(number.bin.len, 2); + CHECK_UNARY(number.hex.begins_with("0x")); + CHECK_UNARY(number.oct.begins_with("0o")); + CHECK_UNARY(number.bin.begins_with("0b")); + } + REQUIRE_GT(C4_COUNTOF(numbers<T>::vals), 2); + // + CHECK_EQ(&front<T>(), numbers<T>::vals + 0); + CHECK_EQ(&front<T>(0), numbers<T>::vals + 0); + CHECK_EQ(&front<T>(1), numbers<T>::vals + 1); + // + CHECK_EQ(&back<T>(), numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 1); + CHECK_EQ(&back<T>(0), numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 1); + CHECK_EQ(&back<T>(1), numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 2); + // + CHECK_EQ(&next(front<T>() ), numbers<T>::vals + 1); + CHECK_EQ(&next(front<T>(), T(1)), numbers<T>::vals + 1); + CHECK_EQ(&next(front<T>(), T(2)), numbers<T>::vals + 2); + // + CHECK_EQ(&next(back<T>() ), numbers<T>::vals + 0); + CHECK_EQ(&next(back<T>(), T(1)), numbers<T>::vals + 0); + CHECK_EQ(&next(back<T>(), T(2)), numbers<T>::vals + 1); + CHECK_EQ(&next(back<T>(), T(3)), numbers<T>::vals + 2); + // + CHECK_EQ(&prev(front<T>()), numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 1); + CHECK_EQ(&prev(front<T>(), T(1)), numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 1); + CHECK_EQ(&prev(front<T>(), T(2)), numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 2); + // + CHECK_EQ(&prev(back<T>()), numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 2); + CHECK_EQ(&prev(back<T>(), T(1)), numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 2); + CHECK_EQ(&prev(back<T>(), T(2)), numbers<T>::vals + C4_COUNTOF(numbers<T>::vals) - 3); +} + +TEST_CASE("test_util.overflow_by") +{ + char buf_[128]; + substr buf = buf_; + REQUIRE_EQ(overflow_by<int8_t>(buf, INT8_C(127), INT8_C(0), INT8_C(10)), "127"); + REQUIRE_EQ(overflow_by<int8_t>(buf, INT8_C(127), INT8_C(0), INT8_C(16)), "0x7f"); + REQUIRE_EQ(overflow_by<int8_t>(buf, INT8_C(127), INT8_C(1), INT8_C(10)), "128"); + REQUIRE_EQ(overflow_by<int8_t>(buf, INT8_C(127), INT8_C(1), INT8_C(16)), "0x80"); + REQUIRE_EQ(overflow_by<int8_t>(buf, INT8_C(127), INT8_C(2), INT8_C(10)), "129"); + REQUIRE_EQ(overflow_by<int8_t>(buf, INT8_C(127), INT8_C(2), INT8_C(16)), "0x81"); +} + +TEST_CASE("test_util.underflow_by") +{ + char buf_[128]; + substr buf = buf_; + REQUIRE_EQ(underflow_by<int8_t>(buf, INT8_C(-128), INT8_C(0), INT8_C(10)), "-128"); + REQUIRE_EQ(underflow_by<int8_t>(buf, INT8_C(-128), INT8_C(0), INT8_C(16)), "-0x80"); + REQUIRE_EQ(underflow_by<int8_t>(buf, INT8_C(-128), INT8_C(1), INT8_C(10)), "-129"); + REQUIRE_EQ(underflow_by<int8_t>(buf, INT8_C(-128), INT8_C(1), INT8_C(16)), "-0x81"); + REQUIRE_EQ(underflow_by<int8_t>(buf, INT8_C(-128), INT8_C(2), INT8_C(10)), "-130"); + REQUIRE_EQ(underflow_by<int8_t>(buf, INT8_C(-128), INT8_C(2), INT8_C(16)), "-0x82"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("digits_dec", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + CHECK_EQ(digits_dec(number.val), nopfx(number.dec).len); + } +} + +TEST_CASE_TEMPLATE("digits_hex", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + CHECK_EQ(digits_hex(number.val), nopfx(number.hex).len); + } +} + +TEST_CASE_TEMPLATE("digits_oct", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + CHECK_EQ(digits_oct(number.val), nopfx(number.oct).len); + } +} + + +TEST_CASE_TEMPLATE("digits_bin", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + CHECK_EQ(digits_bin(number.val), nopfx(number.bin).len); + } +} + + +//----------------------------------------------------------------------------- +TEST_CASE_TEMPLATE("write_dec_unchecked", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + unsigned digits = digits_dec(number.val); + REQUIRE_GE(buf.len, digits); + write_dec_unchecked(buf, number.val, digits); + CHECK_EQ(buf.first(digits), nopfx(number.dec)); + } +} + +TEST_CASE_TEMPLATE("write_hex_unchecked", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + unsigned digits = digits_hex(number.val); + REQUIRE_GE(buf.len, digits); + write_hex_unchecked(buf, number.val, digits); + CHECK_EQ(buf.first(digits), nopfx(number.hex)); + } +} + +TEST_CASE_TEMPLATE("write_oct_unchecked", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + unsigned digits = digits_hex(number.val); + REQUIRE_GE(buf.len, digits); + write_hex_unchecked(buf, number.val, digits); + CHECK_EQ(buf.first(digits), nopfx(number.hex)); + } +} + +TEST_CASE_TEMPLATE("write_bin_unchecked", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + unsigned digits = digits_bin(number.val); + REQUIRE_GE(buf.len, digits); + write_bin_unchecked(buf, number.val, digits); + CHECK_EQ(buf.first(digits), nopfx(number.bin)); + } +} + + +//----------------------------------------------------------------------------- +TEST_CASE_TEMPLATE("write_dec", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + REQUIRE_GE(buf.len, number.dec.len); + size_t retn = write_dec(substr{}, number.val); + CHECK_EQ(retn, number.dec.len); + size_t retb = write_dec(buf, number.val); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, number.dec.len); + CHECK_EQ(buf.first(retb), number.dec); + } +} + +TEST_CASE_TEMPLATE("write_hex", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + REQUIRE_GE(buf.len, number.hex.sub(2).len); + size_t retn = write_hex(substr{}, number.val); + CHECK_EQ(retn, number.hex.sub(2).len); + size_t retb = write_hex(buf, number.val); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, number.hex.sub(2).len); + CHECK_EQ(buf.first(retb), number.hex.sub(2)); + } +} + +TEST_CASE_TEMPLATE("write_oct", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + REQUIRE_GE(buf.len, number.oct.sub(2).len); + size_t retn = write_oct(substr{}, number.val); + CHECK_EQ(retn, number.oct.sub(2).len); + size_t retb = write_oct(buf, number.val); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, number.oct.sub(2).len); + CHECK_EQ(buf.first(retb), nopfx(number.oct)); + } +} + +TEST_CASE_TEMPLATE("write_bin", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + REQUIRE_GE(buf.len, number.bin.sub(2).len); + size_t retb = write_bin(substr{}, number.val); + CHECK_EQ(retb, number.bin.sub(2).len); + size_t retn = write_bin(buf, number.val); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, number.bin.sub(2).len); + CHECK_EQ(buf.first(retb), nopfx(number.bin)); + } +} + + +//----------------------------------------------------------------------------- +TEST_CASE_TEMPLATE("write_dec_digits", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("num digits smaller than length") + { + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + for(int less_ : {0, 1, 2, 4, 8, (int)number.dec.len}) + { + size_t less = (size_t) less_; + if(less > number.dec.len) + continue; + size_t num_digits = number.dec.len - less; + INFO("less=" << less << " num_digits=" << num_digits); + size_t retn = write_dec(substr{}, number.val, num_digits); + CHECK_EQ(retn, number.dec.len); // the number must always be written + size_t retb = write_dec(buf, number.val, num_digits); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, number.dec.len); + CHECK_EQ(buf.first(retb), number.dec); + } + } + } + SUBCASE("num digits larger than length") + { + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + for(int more_ : {1, 2, 4, 8}) + { + size_t more = (size_t) more_; + size_t num_digits = number.dec.len + more; + INFO("more=" << more << " num_digits=" << num_digits); + size_t retn = write_dec(substr{}, number.val, num_digits); + CHECK_EQ(retn, num_digits); + size_t retb = write_dec(buf, number.val, num_digits); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, num_digits); + csubstr result = buf.first(retb); + CHECK_EQ(result.last(number.dec.len), number.dec); + if(number.val) + { + CHECK_EQ(result.triml('0'), number.dec); + CHECK_EQ(result.first_not_of('0'), more); + } + else + { + CHECK(result.begins_with('0')); + CHECK_EQ(result.first_not_of('0'), csubstr::npos); + } + } + } + } +} + +TEST_CASE_TEMPLATE("write_hex_digits", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("num digits smaller than length") + { + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + for(int less_ : {0, 1, 2, 4, 8, (int)number.hex.len}) + { + const csubstr hex = nopfx(number.hex); + size_t less = (size_t) less_; + if(less > hex.len) + continue; + size_t num_digits = hex.len - less; + INFO("less=" << less << " num_digits=" << num_digits); + size_t retn = write_hex(substr{}, number.val, num_digits); + CHECK_EQ(retn, hex.len); // the number must always be written + size_t retb = write_hex(buf, number.val, num_digits); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, hex.len); + CHECK_EQ(buf.first(retb), hex); + } + } + } + SUBCASE("num digits larger than length") + { + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + for(int more_ : {1, 2, 4, 8}) + { + const csubstr hex = nopfx(number.hex); + size_t more = (size_t) more_; + size_t num_digits = hex.len + more; + INFO("more=" << more << " num_digits=" << num_digits); + size_t retn = write_hex(substr{}, number.val, num_digits); + CHECK_EQ(retn, num_digits); + size_t retb = write_hex(buf, number.val, num_digits); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retn, num_digits); + csubstr result = buf.first(retn); + CHECK_EQ(result.last(hex.len), hex); + if(number.val) + { + CHECK_EQ(result.triml('0'), hex); + CHECK_EQ(result.first_not_of('0'), more); + } + else + { + CHECK(result.begins_with('0')); + CHECK_EQ(result.first_not_of('0'), csubstr::npos); + } + } + } + } +} + +TEST_CASE_TEMPLATE("write_oct_digits", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("num digits smaller than length") + { + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + for(int less_ : {0, 1, 2, 4, 8, (int)number.oct.len}) + { + const csubstr oct = nopfx(number.oct); + size_t less = (size_t) less_; + if(less > oct.len) + continue; + size_t num_digits = oct.len - less; + INFO("less=" << less << " num_digits=" << num_digits); + size_t retn = write_oct(substr{}, number.val, num_digits); + CHECK_EQ(retn, oct.len); // the number must always be written + size_t retb = write_oct(buf, number.val, num_digits); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, oct.len); + CHECK_EQ(buf.first(retb), oct); + } + } + } + SUBCASE("num digits larger than length") + { + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + for(int more_ : {1, 2, 4, 8}) + { + const csubstr oct = nopfx(number.oct); + size_t more = (size_t) more_; + size_t num_digits = oct.len + more; + INFO("more=" << more << " num_digits=" << num_digits); + size_t retn = write_oct(substr{}, number.val, num_digits); + CHECK_EQ(retn, num_digits); + size_t retb = write_oct(buf, number.val, num_digits); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, num_digits); + csubstr result = buf.first(retb); + CHECK_EQ(result.last(oct.len), oct); + if(number.val) + { + CHECK_EQ(result.triml('0'), oct); + CHECK_EQ(result.first_not_of('0'), more); + } + else + { + CHECK(result.begins_with('0')); + CHECK_EQ(result.first_not_of('0'), csubstr::npos); + } + } + } + } +} + +TEST_CASE_TEMPLATE("write_bin_digits", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("num digits smaller than length") + { + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + for(int less_ : {0, 1, 2, 4, 8, (int)number.bin.len}) + { + const csubstr bin = nopfx(number.bin); + size_t less = (size_t) less_; + if(less > bin.len) + continue; + size_t num_digits = bin.len - less; + INFO("less=" << less << " num_digits=" << num_digits); + size_t retn = write_bin(substr{}, number.val, num_digits); + CHECK_EQ(retn, bin.len); // the number must always be written + size_t retb = write_bin(buf, number.val, num_digits); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retb, bin.len); + CHECK_EQ(buf.first(retb), bin); + } + } + } + SUBCASE("num digits larger than length") + { + ITER_NUMBERS(T, number) + { + if(number.val < 0) + continue; + INFO(number); + for(int more_ : {1, 2, 4, 8}) + { + const csubstr bin = nopfx(number.bin); + size_t more = (size_t) more_; + size_t num_digits = bin.len + more; + INFO("more=" << more << " num_digits=" << num_digits); + size_t retn = write_bin(substr{}, number.val, num_digits); + CHECK_EQ(retn, num_digits); + size_t retb = write_bin(buf, number.val, num_digits); + CHECK_EQ(retb, retn); + REQUIRE_EQ(retn, num_digits); + csubstr result = buf.first(retn); + CHECK_EQ(result.last(bin.len), bin); + if(number.val) + { + CHECK_EQ(result.triml('0'), bin); + CHECK_EQ(result.first_not_of('0'), more); + } + else + { + CHECK(result.begins_with('0')); + CHECK_EQ(result.first_not_of('0'), csubstr::npos); + } + } + } + } +} + + +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("xtoa", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + INFO(number); + { + buf.fill('?'); + size_t retn = xtoa(substr{}, number.val); + CHECK_EQ(retn, number.dec.len); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK_EQ(buf.first(retb), number.dec); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + } +} + + +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("xtoa_radix.dec", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + INFO(number); + { + buf.fill('?'); + size_t retn = xtoa(substr{}, number.val, T(10)); + CHECK_EQ(retn, number.dec.len); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(10)); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK_EQ(buf.first(retb), number.dec); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + const size_t adj = size_t(number.val < 0); + REQUIRE_LT(adj, number.dec.len); + const size_t dec_digits = number.dec.len - adj; + for(size_t more_digits = 0; more_digits < 6; ++more_digits) + { + buf.fill('?'); + size_t reqdigits = dec_digits + more_digits; + INFO("dec_digits=" << dec_digits << " more_digits=" << more_digits << " req_digits=" << reqdigits); + size_t retn = xtoa(substr{}, number.val, T(10), reqdigits); + CHECK_EQ(retn, reqdigits + size_t(number.val < 0)); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(10), reqdigits); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK(buf.first(retb).ends_with(number.dec.sub(number.val < 0))); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + for(size_t less_digits = 0; less_digits < dec_digits; ++less_digits) + { + buf.fill('?'); + size_t reqdigits = dec_digits - less_digits; + INFO("dec_digits=" << dec_digits << " less_digits=" << less_digits << " req_digits=" << reqdigits); + size_t retn = xtoa(substr{}, number.val, T(10), reqdigits); + CHECK_EQ(retn, number.dec.len); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(10), reqdigits); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK(buf.first(retb).ends_with(number.dec.sub(number.val < 0))); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + } +} + +TEST_CASE_TEMPLATE("xtoa_radix.hex", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + INFO(number); + { + buf.fill('?'); + size_t retn = xtoa(substr{}, number.val, T(16)); + CHECK_EQ(retn, number.hex.len); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(16)); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK_EQ(buf.first(retb), number.hex); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + const size_t adj = size_t(number.val < 0) + size_t(2); // 2 for 0x + REQUIRE_LT(adj, number.hex.len); + const size_t hex_digits = number.hex.len - adj; + for(size_t more_digits = 0; more_digits < 6; ++more_digits) + { + buf.fill('?'); + size_t reqdigits = hex_digits + more_digits; + INFO("more_digits=" << more_digits << " reqdigits=" << reqdigits); + size_t retn = xtoa(substr{}, number.val, T(16), reqdigits); + CHECK_EQ(retn, reqdigits + adj); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(16), reqdigits); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + csubstr result = buf.first(retb); + csubstr ref = number.hex.sub(adj); + INFO("result=" << result << " ref=" << ref); + if(number.val < 0) + CHECK(buf.first(retb).begins_with('-')); + CHECK(result.ends_with(ref)); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + for(size_t less_digits = 0; less_digits < hex_digits; ++less_digits) + { + buf.fill('?'); + size_t reqdigits = hex_digits - less_digits; + INFO("hex_digits=" << hex_digits << " less_digits=" << less_digits << " req_digits=" << reqdigits); + size_t retn = xtoa(substr{}, number.val, T(16), reqdigits); + CHECK_EQ(retn, number.hex.len); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(16), reqdigits); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK(buf.first(retb).ends_with(number.hex.sub(number.val < 0))); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + } +} + +TEST_CASE_TEMPLATE("xtoa_radix.oct", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + INFO(number); + { + buf.fill('?'); + size_t retn = xtoa(substr{}, number.val, T(8)); + CHECK_EQ(retn, number.oct.len); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(8)); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK_EQ(buf.first(retb), number.oct); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + const size_t adj = size_t(number.val < 0) + size_t(2); // 2 for 0o + REQUIRE_LT(adj, number.oct.len); + const size_t oct_digits = number.oct.len - adj; + for(size_t more_digits = 0; more_digits < 6; ++more_digits) + { + buf.fill('?'); + size_t reqdigits = oct_digits + more_digits; + INFO("more_digits=" << more_digits << " reqdigits=" << reqdigits); + size_t retn = xtoa(substr{}, number.val, T(8), reqdigits); + CHECK_EQ(retn, reqdigits + adj); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(8), reqdigits); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + csubstr result = buf.first(retb); + csubstr ref = number.oct.sub(adj); + INFO("result=" << result << " ref=" << ref); + if(number.val < 0) + CHECK(buf.first(retb).begins_with('-')); + CHECK(result.ends_with(ref)); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + for(size_t less_digits = 0; less_digits < oct_digits; ++less_digits) + { + buf.fill('?'); + size_t reqdigits = oct_digits - less_digits; + INFO("oct_digits=" << oct_digits << " less_digits=" << less_digits << " req_digits=" << reqdigits); + size_t retn = xtoa(substr{}, number.val, T(8), reqdigits); + CHECK_EQ(retn, number.oct.len); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(8), reqdigits); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK(buf.first(retb).ends_with(number.oct.sub(number.val < 0))); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + } +} + +TEST_CASE_TEMPLATE("xtoa_radix.bin", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + ITER_NUMBERS(T, number) + { + INFO(number); + { + buf.fill('?'); + size_t retn = xtoa(substr{}, number.val, T(2)); + CHECK_EQ(retn, number.bin.len); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(2)); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK_EQ(buf.first(retb), number.bin); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + const size_t adj = size_t(number.val < 0) + size_t(2); // 2 for 0b + REQUIRE_LT(adj, number.bin.len); + const size_t bin_digits = number.bin.len - adj; + for(size_t more_digits = 0; more_digits < 6; ++more_digits) + { + buf.fill('?'); + size_t reqdigits = bin_digits + more_digits; + INFO("more_digits=" << more_digits << " reqdigits=" << reqdigits); + size_t retn = xtoa(substr{}, number.val, T(2), reqdigits); + CHECK_EQ(retn, reqdigits + adj); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(2), reqdigits); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + csubstr result = buf.first(retb); + csubstr ref = number.bin.sub(adj); + INFO("result=" << result << " ref=" << ref); + if(number.val < 0) + CHECK(buf.first(retb).begins_with('-')); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + for(size_t less_digits = 0; less_digits < bin_digits; ++less_digits) + { + buf.fill('?'); + size_t reqdigits = bin_digits - less_digits; + INFO("bin_digits=" << bin_digits << " less_digits=" << less_digits << " req_digits=" << reqdigits); + size_t retn = xtoa(substr{}, number.val, T(2), reqdigits); + CHECK_EQ(retn, number.bin.len); + CHECK_UNARY(buf.begins_with('?') && buf.first_not_of('?') == csubstr::npos); + size_t retb = xtoa(buf, number.val, T(2), reqdigits); + CHECK_EQ(retn, retb); + REQUIRE_LE(retb, buf.len); + CHECK(buf.first(retb).ends_with(number.bin.sub(number.val < 0))); + T after_roundtrip = number.val + T(1); + CHECK(atox(buf.first(retb), &after_roundtrip)); + CHECK_EQ(after_roundtrip, number.val); + } + } +} + + +//----------------------------------------------------------------------------- + + +TEST_CASE_TEMPLATE("overflows.in_range_does_not_overflow", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("dec") + { + ITER_NUMBERS(T, number) + { + INFO(number); + CHECK_FALSE(overflows<T>(number.dec)); + CHECK_FALSE(overflows<T>(capitalize(buf, number.dec))); + for(size_t numz : {1u, 4u, 6u}) + { + substr buf2 = zpad(buf, number.dec, numz); + CHECK_FALSE(overflows<T>(buf2)); + buf2.toupper(); + CHECK_FALSE(overflows<T>(buf2)); + } + } + } + SUBCASE("hex") + { + ITER_NUMBERS(T, number) + { + INFO(number); + CHECK_FALSE(overflows<T>(number.hex)); + CHECK_FALSE(overflows<T>(capitalize(buf, number.hex))); + for(size_t numz : {1u, 4u, 6u}) + { + substr buf2 = zpad(buf, number.hex, numz); + CHECK_FALSE(overflows<T>(buf2)); + buf2.toupper(); + CHECK_FALSE(overflows<T>(buf2)); + } + } + } + SUBCASE("oct") + { + ITER_NUMBERS(T, number) + { + INFO(number); + CHECK_FALSE(overflows<T>(number.oct)); + CHECK_FALSE(overflows<T>(capitalize(buf, number.oct))); + for(size_t numz : {1u, 4u, 6u}) + { + substr buf2 = zpad(buf, number.oct, numz); + CHECK_FALSE(overflows<T>(buf2)); + buf2.toupper(); + CHECK_FALSE(overflows<T>(buf2)); + } + } + } + SUBCASE("bin") + { + ITER_NUMBERS(T, number) + { + INFO(number); + CHECK_FALSE(overflows<T>(number.bin)); + CHECK_FALSE(overflows<T>(capitalize(buf, number.bin))); + for(size_t numz : {1u, 4u, 6u}) + { + substr buf2 = zpad(buf, number.bin, numz); + CHECK_FALSE(overflows<T>(buf2)); + buf2.toupper(); + CHECK_FALSE(overflows<T>(buf2)); + } + } + } +} + + +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("read_dec", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("numbers") + { + ITER_NUMBERS(T, number) + { + if(number.val < T(0)) + continue; + INFO(number); + { + T val = number.val + T(1); + CHECK(read_dec(number.dec, &val)); + CHECK_EQ(val, number.val); + } + // capitalize + { + T val = number.val + T(1); + csubstr cbuf = capitalize(buf, number.dec); + CHECK(read_dec(cbuf, &val)); + CHECK_EQ(val, number.val); + } + // zero-prefix + for(size_t numz : {1u, 4u, 6u}) + { + T val = number.val + T(1); + substr buf2 = zpad(buf, number.dec, numz); + INFO("zprefix=" << buf2); + CHECK(read_dec(buf2, &val)); + CHECK_EQ(val, number.val); + buf2.toupper(); + CHECK(read_dec(buf2, &val)); + CHECK_EQ(val, number.val); + } + } + } + SUBCASE("fail") + { + T val = {}; + for(auto ic : invalid_cases) + { + if(ic.dec.empty()) + continue; + INFO(ic.dec); + CHECK(!read_dec(ic.dec, &val)); + } + } +} + +TEST_CASE_TEMPLATE("read_hex", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("numbers") + { + ITER_NUMBERS(T, number) + { + if(number.val < T(0)) + continue; + INFO(number); + // must not accept 0x prefix + { + T val = number.val + T(1); + CHECK(!read_hex(number.hex, &val)); + } + // must accept without prefix + csubstr hex = nopfx(number.hex); + INFO("nopfx(hex)=" << hex); + { + T val = number.val + T(1); + CHECK(read_hex(hex, &val)); + CHECK_EQ(val, number.val); + } + // capitalize + { + csubstr cbuf = capitalize(buf, hex); + INFO("capitalized=" << buf); + REQUIRE_EQ(cbuf.len, hex.len); + T val = number.val + T(1); + CHECK(read_hex(cbuf, &val)); + CHECK_EQ(val, number.val); + } + // zero-prefix + for(size_t numz : {1u, 4u, 6u}) + { + T val = number.val + T(1); + substr zprefix = zpad(buf, hex, numz); + INFO("zprefix='" << zprefix << "'"); + CHECK(read_hex(zprefix, &val)); + CHECK_EQ(val, number.val); + zprefix.toupper(); + CHECK(read_hex(zprefix, &val)); + CHECK_EQ(val, number.val); + } + } + } + SUBCASE("fail") + { + char buf2_[128] = {}; + substr buf2 = buf2_; + size_t icase = 0; + for(auto const& ic : invalid_cases) + { + csubstr cbuf = nopfx(buf, ic.hex); + csubstr cbuf2 = capitalize(buf2, cbuf); + INFO("case#=" << icase << " hex='" << ic.hex << "' nopfx(hex)='" << cbuf << "' capitalize(nopfx(hex))='" << cbuf2 << "'"); + REQUIRE_EQ(cbuf2.len, cbuf.len); + // must not accept 0x prefix + if(ic.hex.len) + { + T val = {}; + CHECK(!read_hex(ic.hex, &val)); + } + // it is invalid; must not accept even without 0x prefix + if(cbuf.len) + { + T val = {}; + CHECK(!read_hex(cbuf, &val)); + } + // capitalize + if(cbuf2.len) + { + T val = {}; + CHECK(!read_hex(cbuf2, &val)); + } + ++icase; + } + } +} + +TEST_CASE_TEMPLATE("read_oct", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("numbers") + { + ITER_NUMBERS(T, number) + { + if(number.val < T(0)) + continue; + INFO(number); + // must not accept 0x prefix + { + T val = number.val + T(1); + CHECK(!read_oct(number.oct, &val)); + } + // must accept without prefix + csubstr oct = nopfx(number.oct); + INFO("nopfx(oct)=" << oct); + { + T val = number.val + T(1); + CHECK(read_oct(oct, &val)); + CHECK_EQ(val, number.val); + } + // capitalize + { + csubstr cbuf = capitalize(buf, oct); + INFO("capitalized=" << buf); + REQUIRE_EQ(cbuf.len, oct.len); + T val = number.val + T(1); + CHECK(read_oct(cbuf, &val)); + CHECK_EQ(val, number.val); + } + // zero-prefix + for(size_t numz : {1u, 4u, 6u}) + { + T val = number.val + T(1); + substr zprefix = zpad(buf, oct, numz); + INFO("zprefix=" << zprefix); + CHECK(read_oct(zprefix, &val)); + CHECK_EQ(val, number.val); + zprefix.toupper(); + CHECK(read_oct(zprefix, &val)); + CHECK_EQ(val, number.val); + } + } + } + SUBCASE("fail") + { + char buf2_[128] = {}; + substr buf2 = buf2_; + size_t icase = 0; + for(auto const& ic : invalid_cases) + { + csubstr cbuf = nopfx(buf, ic.oct); + csubstr cbuf2 = capitalize(buf2, cbuf); + INFO("case#=" << icase << " oct='" << ic.oct << "' nopfx(oct)='" << cbuf << "' capitalize(nopfx(oct))='" << cbuf2 << "'"); + REQUIRE_EQ(cbuf2.len, cbuf.len); + // must not accept 0x prefix + if(ic.oct.len) + { + T val = {}; + CHECK(!read_oct(ic.oct, &val)); + } + // it is invalid; must not accept even without 0x prefix + if(cbuf.len) + { + T val = {}; + CHECK(!read_oct(cbuf, &val)); + } + // capitalize + if(cbuf2.len) + { + T val = {}; + CHECK(!read_oct(cbuf2, &val)); + } + ++icase; + } + } +} + +TEST_CASE_TEMPLATE("read_bin", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("numbers") + { + ITER_NUMBERS(T, number) + { + if(number.val < T(0)) + continue; + INFO(number); + // must not accept 0x prefix + { + T val = number.val + T(1); + CHECK(!read_bin(number.bin, &val)); + } + // must accept without prefix + csubstr bin = nopfx(number.bin); + INFO("nopfx(bin)=" << bin); + { + T val = number.val + T(1); + CHECK(read_bin(bin, &val)); + CHECK_EQ(val, number.val); + } + // capitalize + { + csubstr cbuf = capitalize(buf, bin); + INFO("capitalized=" << buf); + REQUIRE_EQ(cbuf.len, bin.len); + T val = number.val + T(1); + CHECK(read_bin(cbuf, &val)); + CHECK_EQ(val, number.val); + } + // zero-prefix + for(size_t numz : {1u, 4u, 6u}) + { + T val = number.val + T(1); + substr zprefix = zpad(buf, bin, numz); + INFO("zprefix=" << zprefix); + CHECK(read_bin(zprefix, &val)); + CHECK_EQ(val, number.val); + zprefix.toupper(); + CHECK(read_bin(zprefix, &val)); + CHECK_EQ(val, number.val); + } + } + } + SUBCASE("fail") + { + char buf2_[128] = {}; + substr buf2 = buf2_; + size_t icase = 0; + for(auto const& ic : invalid_cases) + { + csubstr cbuf = nopfx(buf, ic.bin); + csubstr cbuf2 = capitalize(buf2, cbuf); + INFO("case#=" << icase << " bin='" << ic.bin << "' nopfx(bin)='" << cbuf << "' capitalize(nopfx(bin))='" << cbuf2 << "'"); + REQUIRE_EQ(cbuf2.len, cbuf.len); + // must not accept 0x prefix + if(ic.bin.len) + { + T val = {}; + CHECK(!read_bin(ic.bin, &val)); + } + // it is invalid; must not accept even without 0x prefix + if(cbuf.len) + { + T val = {}; + CHECK(!read_bin(cbuf, &val)); + } + // capitalize + if(cbuf2.len) + { + T val = {}; + CHECK(!read_bin(cbuf2, &val)); + } + ++icase; + } + } +} + + +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("atox", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("dec") + { + ITER_NUMBERS(T, number) + { + INFO(number); + { + T val = number.val + T(1); + CHECK(atox(number.dec, &val)); + CHECK_EQ(val, number.val); + } + // zero-prefix + for(size_t numz : {1u, 4u, 6u}) + { + T val = number.val + T(1); + substr zprefix = zpad(buf, number.dec, numz); + INFO("zprefix=" << zprefix); + CHECK(atox(zprefix, &val)); + CHECK_EQ(val, number.val); + } + } + } + SUBCASE("hex") + { + ITER_NUMBERS(T, number) + { + INFO(number); + { + T val = number.val + T(1); + CHECK(atox(number.hex, &val)); + CHECK_EQ(val, number.val); + } + // capitalize + { + T val = number.val + T(1); + csubstr cbuf = capitalize(buf, number.hex); + CHECK(atox(cbuf, &val)); + CHECK_EQ(val, number.val); + } + // zero-prefix + for(size_t numz : {1u, 4u, 6u}) + { + T val = number.val + T(1); + substr zprefix = zpad(buf, number.hex, numz); + INFO("zprefix=" << zprefix); + CHECK(atox(zprefix, &val)); + CHECK_EQ(val, number.val); + zprefix.toupper(); + CHECK(atox(zprefix, &val)); + CHECK_EQ(val, number.val); + } + } + } + SUBCASE("oct") + { + ITER_NUMBERS(T, number) + { + INFO(number); + { + T val = number.val + T(1); + CHECK(atox(number.oct, &val)); + CHECK_EQ(val, number.val); + } + // capitalize + { + T val = number.val + T(1); + csubstr cbuf = capitalize(buf, number.oct); + CHECK(atox(cbuf, &val)); + CHECK_EQ(val, number.val); + } + // zero-prefix + for(size_t numz : {1u, 4u, 6u}) + { + T val = number.val + T(1); + substr zprefix = zpad(buf, number.oct, numz); + INFO("zprefix=" << zprefix); + CHECK(atox(zprefix, &val)); + CHECK_EQ(val, number.val); + zprefix.toupper(); + CHECK(atox(zprefix, &val)); + CHECK_EQ(val, number.val); + } + } + } + SUBCASE("bin") + { + ITER_NUMBERS(T, number) + { + INFO(number); + { + T val = number.val + T(1); + CHECK(atox(number.bin, &val)); + CHECK_EQ(val, number.val); + } + // capitalize + { + T val = number.val + T(1); + csubstr cbuf = capitalize(buf, number.bin); + CHECK(atox(cbuf, &val)); + CHECK_EQ(val, number.val); + } + // zero-prefix + for(size_t numz : {1u, 4u, 6u}) + { + T val = number.val + T(1); + substr zprefix = zpad(buf, number.oct, numz); + INFO("zprefix=" << zprefix); + CHECK(atox(zprefix, &val)); + CHECK_EQ(val, number.val); + zprefix.toupper(); + CHECK(atox(zprefix, &val)); + CHECK_EQ(val, number.val); + } + } + } +} + +TEST_CASE_TEMPLATE("atox.fail", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + SUBCASE("dec") + { + size_t icase = 0; + for(auto const& ic : invalid_cases) + { + csubstr cdec = capitalize(buf, ic.dec); + INFO("case#=" << icase << " dec='" << ic.dec << "' capitalize='" << cdec << "'"); + REQUIRE_EQ(cdec.len, ic.dec.len); + { + T val = {}; + CHECK(!atox(ic.dec, &val)); + } + { + T val = {}; + CHECK(!atox(cdec, &val)); + } + ++icase; + } + } + SUBCASE("hex") + { + size_t icase = 0; + for(auto const& ic : invalid_cases) + { + csubstr chex = capitalize(buf, ic.hex); + INFO("case#=" << icase << " hex='" << ic.hex << "' capitalize='" << chex << "'"); + REQUIRE_EQ(chex.len, ic.hex.len); + { + T val = {}; + CHECK(!atox(ic.hex, &val)); + } + { + T val = {}; + CHECK(!atox(chex, &val)); + } + ++icase; + } + } + SUBCASE("oct") + { + size_t icase = 0; + for(auto const& ic : invalid_cases) + { + csubstr coct = capitalize(buf, ic.oct); + INFO("case#=" << icase << " oct='" << ic.oct << "' capitalize='" << coct << "'"); + REQUIRE_EQ(coct.len, ic.oct.len); + { + T val = {}; + CHECK(!atox(ic.oct, &val)); + } + { + T val = {}; + CHECK(!atox(coct, &val)); + } + ++icase; + } + } + SUBCASE("bin") + { + size_t icase = 0; + for(auto const& ic : invalid_cases) + { + csubstr cbin = capitalize(buf, ic.bin); + INFO("case#=" << icase << " bin='" << ic.bin << "' capitalize='" << cbin << "'"); + REQUIRE_EQ(cbin.len, ic.bin.len); + { + T val = {}; + CHECK(!atox(ic.bin, &val)); + } + { + T val = {}; + CHECK(!atox(cbin, &val)); + } + ++icase; + } + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T> +void test_overflows(std::initializer_list<const char *> args) +{ + for(const char *s : args) + CHECK_MESSAGE(overflows<T>(to_csubstr(s)), "num=" << s); +} + +template<class T> +void test_no_overflows(std::initializer_list<const char *> args) +{ + for(const char *s : args) + CHECK_MESSAGE(!overflows<T>(to_csubstr(s)), "num=" << s); +} + +template<class T> +auto test_no_overflow_zeroes() + -> typename std::enable_if<std::is_signed<T>::value, void>::type +{ + test_no_overflows<T>({ "-", "-0", "-000", "-0b0", "-0B0", "-0x0", "-0X0", "-0o0", "-0O0" }); + test_no_overflows<T>({ "", "0", "000", "0b0", "0B0", "0x0", "0X0", "0o0", "0O0" }); +} + +template<class T> +auto test_no_overflow_zeroes() + -> typename std::enable_if<std::is_unsigned<T>::value, void>::type +{ + test_no_overflows<T>({ "", "0", "000", "0b0", "0B0", "0x0", "0X0", "0o0", "0O0" }); +} + + +// test overflow in sizes smaller than 64 bit by upcasting +TEST_CASE_TEMPLATE("atox.overflow", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t) +{ + char buf_[128]; + substr buf = buf_; + auto do_test = [](bool is_overflow, number_case<T> const& num, csubstr exceeded, number_case<T> const& wrapped){ + char buf2_[128] = {}; + substr buf2 = buf2_; + INFO("exceeded=" << exceeded << " is_overflow=" << is_overflow); + INFO("num=" << num); + INFO("wrapped=" << wrapped); + CHECK_EQ(is_overflow, overflows<T>(exceeded)); + if(is_overflow) + CHECK_NE(&num, &wrapped); + else + CHECK_EQ(&num, &wrapped); + { + T val = num.val + T(1); + CHECK(atox(exceeded, &val)); + CHECK_EQ(val, wrapped.val); + } + // capitalize + buf2 = capitalize(buf2_, exceeded); + INFO(buf2); + CHECK_EQ(is_overflow, overflows<T>(buf2)); + { + T val = num.val + T(1); + CHECK(atox(buf2, &val)); + CHECK_EQ(val, wrapped.val); + } + // zero-pad on the left + for(size_t numz : {1u, 4u, 6u}) + { + buf2 = zpad(buf2_, exceeded, numz); + CHECK_EQ(is_overflow, overflows<T>(buf2)); + { + T val = num.val + T(1); + CHECK(atox(buf2, &val)); + CHECK_EQ(val, wrapped.val); + } + buf2.toupper(); + CHECK_EQ(is_overflow, overflows<T>(buf2)); + { + T val = num.val + T(1); + CHECK(atox(buf2, &val)); + CHECK_EQ(val, wrapped.val); + } + } + }; + auto do_test_overflow = [&](T exceed_how_much, T radix){ + REQUIRE(exceed_how_much >= 0); + number_case<T> const& backelm = back<T>(); + number_case<T> const& wrapelm = next(backelm, (size_t)exceed_how_much); + csubstr exceeded = overflow_by(buf, backelm.val, exceed_how_much, radix); + do_test(exceed_how_much > 0, backelm, exceeded, wrapelm); + }; + auto do_test_underflow = [&](T exceed_how_much, T radix){ + REQUIRE(exceed_how_much >= 0); + number_case<T> const& frntelm = front<T>(); + number_case<T> const& wrapelm = prev(frntelm, (size_t)exceed_how_much); + csubstr exceeded = underflow_by(buf, frntelm.val, exceed_how_much, radix); + do_test(exceed_how_much > 0, frntelm, exceeded, wrapelm); + }; + SUBCASE("zeroes") + { + test_no_overflow_zeroes<T>(); + } + SUBCASE("dec") + { + do_test_underflow(T(0), T(10)); + do_test_underflow(T(1), T(10)); + do_test_underflow(T(2), T(10)); + do_test_underflow(T(3), T(10)); + do_test_underflow(T(4), T(10)); + do_test_underflow(T(5), T(10)); + do_test_overflow(T(0), T(10)); + do_test_overflow(T(1), T(10)); + do_test_overflow(T(2), T(10)); + do_test_overflow(T(3), T(10)); + do_test_overflow(T(4), T(10)); + do_test_overflow(T(5), T(10)); + } + SUBCASE("hex") + { + do_test_underflow(T(0), T(16)); + do_test_underflow(T(1), T(16)); + do_test_underflow(T(2), T(16)); + do_test_underflow(T(3), T(16)); + do_test_underflow(T(4), T(16)); + do_test_underflow(T(5), T(16)); + do_test_overflow(T(0), T(16)); + do_test_overflow(T(1), T(16)); + do_test_overflow(T(2), T(16)); + do_test_overflow(T(3), T(16)); + do_test_overflow(T(4), T(16)); + do_test_overflow(T(5), T(16)); + } + SUBCASE("oct") + { + do_test_underflow(T(0), T(8)); + do_test_underflow(T(1), T(8)); + do_test_underflow(T(2), T(8)); + do_test_underflow(T(3), T(8)); + do_test_underflow(T(4), T(8)); + do_test_underflow(T(5), T(8)); + do_test_overflow(T(0), T(8)); + do_test_overflow(T(1), T(8)); + do_test_overflow(T(2), T(8)); + do_test_overflow(T(3), T(8)); + do_test_overflow(T(4), T(8)); + do_test_overflow(T(5), T(8)); + } + SUBCASE("bin") + { + do_test_underflow(T(0), T(2)); + do_test_underflow(T(1), T(2)); + do_test_underflow(T(2), T(2)); + do_test_underflow(T(3), T(2)); + do_test_underflow(T(4), T(2)); + do_test_underflow(T(5), T(2)); + do_test_overflow(T(0), T(2)); + do_test_overflow(T(1), T(2)); + do_test_overflow(T(2), T(2)); + do_test_overflow(T(3), T(2)); + do_test_overflow(T(4), T(2)); + do_test_overflow(T(5), T(2)); + } +} + +TEST_CASE_TEMPLATE("atox.overflow64", T, int64_t, uint64_t) +{ + char buf_[128] = {}; + substr buf = buf_; + auto test_atox = [](csubstr s, overflow64case<T> const& c){ + INFO("s=" << s); + T val = c.wrapped + T(1); + if(std::is_signed<T>::value || !s.begins_with('-')) + { + CHECK(atox(s, &val)); + CHECK_EQ(val, c.wrapped); + } + else + { + CHECK(!atox(s, &val)); + } + }; + SUBCASE("zeroes") + { + test_no_overflow_zeroes<T>(); + } + SUBCASE("dec") + { + for(auto c : overflow64cases<T>::values) + { + INFO(c.dec); + CHECK_EQ(c.is_overflow, overflows<T>(c.dec)); + test_atox(c.dec, c); + csubstr capitalized = capitalize(buf, c.dec); + CHECK_EQ(c.is_overflow, overflows<T>(capitalized)); + test_atox(capitalized, c); + for(size_t numz : {1u, 4u, 6u}) + { + substr buf2 = zpad(buf, c.dec, numz); + CHECK_EQ(c.is_overflow, overflows<T>(buf2)); + test_atox(buf2, c); + buf2.toupper(); + CHECK_EQ(c.is_overflow, overflows<T>(buf2)); + test_atox(buf2, c); + } + } + } + SUBCASE("hex") + { + for(auto c : overflow64cases<T>::values) + { + INFO(c.hex); + CHECK_EQ(c.is_overflow, overflows<T>(c.hex)); + test_atox(c.hex, c); + csubstr capitalized = capitalize(buf, c.hex); + CHECK_EQ(c.is_overflow, overflows<T>(capitalized)); + test_atox(capitalized, c); + for(size_t numz : {1u, 4u, 6u}) + { + substr buf2 = zpad(buf, c.hex, numz); + CHECK_EQ(c.is_overflow, overflows<T>(buf2)); + test_atox(buf2, c); + buf2.toupper(); + CHECK_EQ(c.is_overflow, overflows<T>(buf2)); + test_atox(buf2, c); + } + } + } + SUBCASE("oct") + { + for(auto c : overflow64cases<T>::values) + { + INFO(c.oct); + CHECK_EQ(c.is_overflow, overflows<T>(c.oct)); + test_atox(c.oct, c); + csubstr capitalized = capitalize(buf, c.oct); + CHECK_EQ(c.is_overflow, overflows<T>(capitalized)); + test_atox(capitalized, c); + for(size_t numz : {1u, 4u, 6u}) + { + substr buf2 = zpad(buf, c.oct, numz); + CHECK_EQ(c.is_overflow, overflows<T>(buf2)); + test_atox(buf2, c); + buf2.toupper(); + CHECK_EQ(c.is_overflow, overflows<T>(buf2)); + test_atox(buf2, c); + } + } + } + SUBCASE("bin") + { + for(auto c : overflow64cases<T>::values) + { + INFO(c.bin); + CHECK_EQ(c.is_overflow, overflows<T>(c.bin)); + test_atox(c.bin, c); + csubstr capitalized = capitalize(buf, c.bin); + CHECK_EQ(c.is_overflow, overflows<T>(capitalized)); + test_atox(capitalized, c); + for(size_t numz : {1u, 4u, 6u}) + { + substr buf2 = zpad(buf, c.bin, numz); + CHECK_EQ(c.is_overflow, overflows<T>(buf2)); + test_atox(buf2, c); + buf2.toupper(); + CHECK_EQ(c.is_overflow, overflows<T>(buf2)); + test_atox(buf2, c); + } + } + } +} + + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T> +void test_overflows_hex() +{ + T x = {}; + std::string str; + if (std::is_unsigned<T>::value) + { + /* with leading zeroes */ + str = "0x0" + std::string(sizeof (T) * 2, 'F'); + CHECK_MESSAGE(!overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::max(), x); + + str = "0x01" + std::string(sizeof (T) * 2, '0'); + CHECK_MESSAGE(overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::min(), x); + } + else + { + /* with leading zeroes */ + str = "0x07" + std::string(sizeof (T) * 2 - 1, 'F'); + CHECK_MESSAGE(!overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::max(), x); + + str = "0x0" + std::string(sizeof (T) * 2, 'F'); + CHECK_MESSAGE(overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(-1, x); + + str = "-0x08" + std::string(sizeof (T) * 2 - 1, '0'); + CHECK_MESSAGE(!overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::min(), x); + + str = "-0x08" + std::string(sizeof (T) * 2 - 2, '0') + "1"; + CHECK_MESSAGE(overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::max(), x); + } +} + +template<class T> +void test_overflows_bin() +{ + T x = {}; + std::string str; + if (std::is_unsigned<T>::value) + { + /* with leading zeroes */ + str = "0b0" + std::string(sizeof (T) * 8, '1'); + CHECK_MESSAGE(!overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::max(), x); + + str = "0b01" + std::string(sizeof (T) * 8, '0'); + CHECK_MESSAGE(overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::min(), x); + } + else + { + /* with leading zeroes */ + str = "0b0" + std::string(sizeof (T) * 8 - 1, '1'); + CHECK_MESSAGE(!overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::max(), x); + + str = "0b0" + std::string(sizeof (T) * 8, '1'); + CHECK_MESSAGE(overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(-1, x); + + str = "-0b01" + std::string(sizeof (T) * 8 - 1, '0'); + CHECK_MESSAGE(!overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::min(), x); + + str = "-0b01" + std::string(sizeof (T) * 8 - 2, '0') + "1"; + CHECK_MESSAGE(overflows<T>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<T>::max(), x); + } +} + +// TODO: test_overflows_oct + +template<class T> +typename std::enable_if<std::is_unsigned<T>::value, void>::type +test_overflows() +{ + for(int radix : { 2, 8, 10, 16 }) + { + char bufc[100] = {0}; + substr s(bufc); + INFO("radix=" << radix << " num=" << s); + + uint64_t max = (uint64_t) std::numeric_limits<T>::max(); + size_t sz = utoa<uint64_t>(s, max, (uint64_t)radix); + REQUIRE_LE(sz, s.size()); + CHECK(!overflows<T>(s.first(sz))); + memset(s.str, 0, s.len); + sz = utoa<uint64_t>(s, max + 1, (uint64_t)radix); + REQUIRE_LE(sz, s.size()); + CHECK(overflows<T>(s.first(sz))); + } + + test_overflows_hex<T>(); + test_overflows_bin<T>(); + // TODO: octal +} + +template<class T> +typename std::enable_if<std::is_signed<T>::value, void>::type +test_overflows() +{ + for(int radix : { 2, 8, 10, 16 }) + { + char bufc[100] = {0}; + substr s(bufc); + INFO("radix=" << radix << " num=" << s); + + int64_t max = (int64_t) std::numeric_limits<T>::max(); + size_t sz = itoa<int64_t>(s, max, (int64_t)radix); + REQUIRE_LE(sz, s.size()); + CHECK(!overflows<T>(s.first(sz))); + memset(s.str, 0, s.len); + sz = itoa<int64_t>(s, max + 1, (int64_t)radix); + REQUIRE_LE(sz, s.size()); + CHECK(overflows<T>(s.first(sz))); + + int64_t min = (int64_t) std::numeric_limits<T>::min(); + sz = itoa<int64_t>(s, min, (int64_t)radix); + REQUIRE_LE(sz, s.size()); + CHECK(!overflows<T>(s.first(sz))); + memset(s.str, 0, s.len); + sz = itoa<int64_t>(s, min - 1, (int64_t)radix); + REQUIRE_LE(sz, s.size()); + CHECK(overflows<T>(s.first(sz))); + } + + test_overflows_hex<T>(); + test_overflows_bin<T>(); + // TODO: octal +} + +TEST_CASE_TEMPLATE("overflows.8bit_32bit", T, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t) +{ + test_overflows<T>(); +} + +TEST_CASE("overflows.u64") +{ + CHECK(!overflows<uint64_t>("18446744073709551614")); + CHECK(!overflows<uint64_t>("18446744073709551615")); + CHECK(overflows<uint64_t>("18446744073709551616")); + + // more chars but leading zeroes + CHECK(!overflows<uint64_t>("0018446744073709551615")); + + { /* with leading zeroes */ + std::string str; + uint64_t x = {}; + str = "0o01" + std::string(21, '7'); + CHECK_MESSAGE(!overflows<uint64_t>(to_csubstr(str)), "num=" << str); + str = "0o02" + std::string(21, '0'); + CHECK_MESSAGE(overflows<uint64_t>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(0, x); + } + + test_overflows_hex<uint64_t>(); + test_overflows_bin<uint64_t>(); +} + +TEST_CASE("overflows.i64") +{ + CHECK(!overflows<int64_t>("9223372036854775806")); + CHECK(!overflows<int64_t>("9223372036854775807")); + CHECK(overflows<int64_t>("9223372036854775808")); + CHECK(!overflows<int64_t>("-9223372036854775808")); + CHECK(overflows<int64_t>("-9223372036854775809")); + + // more chars, but leading zeroes + CHECK(!overflows<int64_t>("0009223372036854775807")); + CHECK(!overflows<int64_t>("-0009223372036854775807")); + + { /* with leading zeroes */ + std::string str; + int64_t x = {}; + str = "0o0" + std::string(21, '7'); + CHECK_MESSAGE(!overflows<int64_t>(to_csubstr(str)), "num=" << str); + str = "0o01" + std::string(21, '0'); + CHECK_MESSAGE(overflows<int64_t>(to_csubstr(str)), "num=" << str); + CHECK(atox(to_csubstr(str), &x)); + CHECK_EQ(std::numeric_limits<int64_t>::min(), x); + } + + test_overflows_hex<int64_t>(); + test_overflows_bin<int64_t>(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +/** remove trailing digits after precision */ +template<class T> +T remprec10(T val, int precision) +{ + T fprec = T(1); + for(int i = 0; i < precision; ++i) + fprec *= T(10); + T rval = val * fprec; + return ((T)((int64_t)rval)) / fprec; +} + +template<class T> +T test_ator(csubstr s, T ref) +{ + INFO("str=" << s << " ref=" << ref); + T rval; + CHECK(atox(s, &rval)); + INFO("rval=" << rval); + CHECK_EQ(memcmp(&rval, &ref, sizeof(T)), 0); + return rval; +} + +template<class Real> +void test_rtoa(substr buf, Real f, int precision, const char *scient, const char *flt, const char* flex, const char *hexa, const char *hexa_alternative=nullptr) +{ + size_t ret; + Real pf = remprec10(f, precision); + + { + INFO("num=" << f << " precision=" << precision << " scient=" << scient); + memset(buf.str, 0, buf.len); + ret = xtoa(buf, f, precision, FTOA_SCIENT); + REQUIRE_LE(ret, buf.len); + CHECK_EQ(buf.first(ret), to_csubstr(scient)); + test_ator(buf.first(ret), pf); + } + + { + INFO("num=" << f << " precision=" << precision << " flt=" << flt); + memset(buf.str, 0, ret); + ret = xtoa(buf, f, precision, FTOA_FLOAT); + REQUIRE_LE(ret, buf.len); + CHECK_EQ(buf.first(ret), to_csubstr(flt)); + test_ator(buf.first(ret), pf); + } + + { + INFO("num=" << f << " precision=" << precision << " flex=" << flex); + memset(buf.str, 0, ret); + ret = xtoa(buf, f, precision+1, FTOA_FLEX); + REQUIRE_LE(ret, buf.len); + CHECK_EQ(buf.first(ret), to_csubstr(flex)); + test_ator(buf.first(ret), pf); + } + + { + if(!hexa_alternative) + hexa_alternative = hexa; + INFO("num=" << f << " precision=" << precision << " hexa=" << hexa << " hexa_alternative=" << hexa_alternative); + memset(buf.str, 0, ret); + ret = xtoa(buf, f, precision, FTOA_HEXA); + REQUIRE_LE(ret, buf.len); + INFO("buf='" << buf.first(ret) << "'"); + + CHECK((buf.first(ret) == to_csubstr(hexa) || buf.first(ret) == to_csubstr(hexa_alternative))); + Real readback = {}; + CHECK(atox(buf.first(ret), &readback)); + INFO("readback=" << readback); + REQUIRE_EQ(xtoa(buf, readback, precision, FTOA_HEXA), ret); + Real readback2 = {}; + CHECK(atox(buf.first(ret), &readback2)); + INFO("readback2=" << readback2); + CHECK_EQ(memcmp(&readback2, &readback, sizeof(Real)), 0); + } +} + + +TEST_CASE("ftoa.basic") +{ + char bufc[128]; + substr buf(bufc); + C4_ASSERT(buf.len == sizeof(bufc)-1); + + // earlier versions of emscripten's sprintf() do not respect some + // precision values when printing in hexadecimal format. + // + // @see https://github.com/biojppm/c4core/pull/52 + #if defined(__EMSCRIPTEN__) && __EMSCRIPTEN_major__ < 3 + #define _c4emscripten_alt(alt) , alt + #define _c4emscripten_alt2(alt1, alt2) , alt2 + #else + #define _c4emscripten_alt(alt) + #define _c4emscripten_alt2(alt1, alt2) , alt1 + #endif + + float f = 1.1234123f; + double d = 1.1234123; + + test_rtoa(buf, f, 0, /*scient*/"1e+00", /*flt*/"1", /*flex*/"1", /*hexa*/"0x1p+0"); + test_rtoa(buf, d, 0, /*scient*/"1e+00", /*flt*/"1", /*flex*/"1", /*hexa*/"0x1p+0"); + + test_rtoa(buf, f, 1, /*scient*/"1.1e+00", /*flt*/"1.1", /*flex*/"1.1", /*hexa*/"0x1.2p+0"); + test_rtoa(buf, d, 1, /*scient*/"1.1e+00", /*flt*/"1.1", /*flex*/"1.1", /*hexa*/"0x1.2p+0"); + + test_rtoa(buf, f, 2, /*scient*/"1.12e+00", /*flt*/"1.12", /*flex*/"1.12", /*hexa*/"0x1.20p+0" _c4emscripten_alt("0x1.1f8p+0")); + test_rtoa(buf, d, 2, /*scient*/"1.12e+00", /*flt*/"1.12", /*flex*/"1.12", /*hexa*/"0x1.20p+0" _c4emscripten_alt("0x1.1f8p+0")); + + test_rtoa(buf, f, 3, /*scient*/"1.123e+00", /*flt*/"1.123", /*flex*/"1.123", /*hexa*/"0x1.1f9p+0" _c4emscripten_alt("0x1.1f98p+0")); + test_rtoa(buf, d, 3, /*scient*/"1.123e+00", /*flt*/"1.123", /*flex*/"1.123", /*hexa*/"0x1.1f9p+0" _c4emscripten_alt("0x1.1f98p+0")); + + test_rtoa(buf, f, 4, /*scient*/"1.1234e+00", /*flt*/"1.1234", /*flex*/"1.1234", /*hexa*/"0x1.1f98p+0"); + test_rtoa(buf, d, 4, /*scient*/"1.1234e+00", /*flt*/"1.1234", /*flex*/"1.1234", /*hexa*/"0x1.1f98p+0"); + + f = 1.01234123f; + d = 1.01234123; + + test_rtoa(buf, f, 0, /*scient*/"1e+00", /*flt*/"1", /*flex*/"1", /*hexa*/"0x1p+0"); + test_rtoa(buf, d, 0, /*scient*/"1e+00", /*flt*/"1", /*flex*/"1", /*hexa*/"0x1p+0"); + + test_rtoa(buf, f, 1, /*scient*/"1.0e+00", /*flt*/"1.0", /*flex*/"1", /*hexa*/"0x1.0p+0"); + test_rtoa(buf, d, 1, /*scient*/"1.0e+00", /*flt*/"1.0", /*flex*/"1", /*hexa*/"0x1.0p+0"); + + test_rtoa(buf, f, 2, /*scient*/"1.01e+00", /*flt*/"1.01", /*flex*/"1.01", /*hexa*/"0x1.03p+0"); + test_rtoa(buf, d, 2, /*scient*/"1.01e+00", /*flt*/"1.01", /*flex*/"1.01", /*hexa*/"0x1.03p+0"); + + test_rtoa(buf, f, 3, /*scient*/"1.012e+00", /*flt*/"1.012", /*flex*/"1.012", /*hexa*/"0x1.033p+0" _c4emscripten_alt2("0x1.032p+0", "0x1.0328p+0")); + test_rtoa(buf, d, 3, /*scient*/"1.012e+00", /*flt*/"1.012", /*flex*/"1.012", /*hexa*/"0x1.033p+0" _c4emscripten_alt2("0x1.032p+0", "0x1.0328p+0")); + + test_rtoa(buf, f, 4, /*scient*/"1.0123e+00", /*flt*/"1.0123", /*flex*/"1.0123", /*hexa*/"0x1.0329p+0"); + test_rtoa(buf, d, 4, /*scient*/"1.0123e+00", /*flt*/"1.0123", /*flex*/"1.0123", /*hexa*/"0x1.0329p+0"); + + f = 0.f; + d = 0.; + + test_rtoa(buf, f, 0, /*scient*/"0e+00", /*flt*/"0", /*flex*/"0", /*hexa*/"0x0p+0"); + test_rtoa(buf, d, 0, /*scient*/"0e+00", /*flt*/"0", /*flex*/"0", /*hexa*/"0x0p+0"); + + test_rtoa(buf, f, 1, /*scient*/"0.0e+00", /*flt*/"0.0", /*flex*/"0", /*hexa*/"0x0.0p+0"); + test_rtoa(buf, d, 1, /*scient*/"0.0e+00", /*flt*/"0.0", /*flex*/"0", /*hexa*/"0x0.0p+0"); + + test_rtoa(buf, f, 2, /*scient*/"0.00e+00", /*flt*/"0.00", /*flex*/"0", /*hexa*/"0x0.00p+0"); + test_rtoa(buf, d, 2, /*scient*/"0.00e+00", /*flt*/"0.00", /*flex*/"0", /*hexa*/"0x0.00p+0"); + + test_rtoa(buf, f, 3, /*scient*/"0.000e+00", /*flt*/"0.000", /*flex*/"0", /*hexa*/"0x0.000p+0" _c4emscripten_alt2("0x0.000p+0", "0x0.000p+0")); + test_rtoa(buf, d, 3, /*scient*/"0.000e+00", /*flt*/"0.000", /*flex*/"0", /*hexa*/"0x0.000p+0" _c4emscripten_alt2("0x0.000p+0", "0x0.000p+0")); + + test_rtoa(buf, f, 4, /*scient*/"0.0000e+00", /*flt*/"0.0000", /*flex*/"0", /*hexa*/"0x0.0000p+0"); + test_rtoa(buf, d, 4, /*scient*/"0.0000e+00", /*flt*/"0.0000", /*flex*/"0", /*hexa*/"0x0.0000p+0"); + + f = 1.f; + d = 1.; + + test_rtoa(buf, f, 0, /*scient*/"1e+00", /*flt*/"1", /*flex*/"1", /*hexa*/"0x1p+0"); + test_rtoa(buf, d, 0, /*scient*/"1e+00", /*flt*/"1", /*flex*/"1", /*hexa*/"0x1p+0"); + + test_rtoa(buf, f, 1, /*scient*/"1.0e+00", /*flt*/"1.0", /*flex*/"1", /*hexa*/"0x1.0p+0"); + test_rtoa(buf, d, 1, /*scient*/"1.0e+00", /*flt*/"1.0", /*flex*/"1", /*hexa*/"0x1.0p+0"); + + test_rtoa(buf, f, 2, /*scient*/"1.00e+00", /*flt*/"1.00", /*flex*/"1", /*hexa*/"0x1.00p+0"); + test_rtoa(buf, d, 2, /*scient*/"1.00e+00", /*flt*/"1.00", /*flex*/"1", /*hexa*/"0x1.00p+0"); + + test_rtoa(buf, f, 3, /*scient*/"1.000e+00", /*flt*/"1.000", /*flex*/"1", /*hexa*/"0x1.000p+0" _c4emscripten_alt2("0x1.000p+0", "0x1.000p+0")); + test_rtoa(buf, d, 3, /*scient*/"1.000e+00", /*flt*/"1.000", /*flex*/"1", /*hexa*/"0x1.000p+0" _c4emscripten_alt2("0x1.000p+0", "0x1.000p+0")); + + test_rtoa(buf, f, 4, /*scient*/"1.0000e+00", /*flt*/"1.0000", /*flex*/"1", /*hexa*/"0x1.0000p+0"); + test_rtoa(buf, d, 4, /*scient*/"1.0000e+00", /*flt*/"1.0000", /*flex*/"1", /*hexa*/"0x1.0000p+0"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +TEST_CASE_TEMPLATE("atof.integral", T, float, double) +{ + auto t_ = [](csubstr str, int val){ + T rval = (T)10 * (T)val; + INFO("str=" << str); + bool ret = atox(str, &rval); + CHECK_EQ(ret, true); + CHECK_EQ(static_cast<int>(rval), val); + CHECK_EQ(rval, (T)val); + }; + + csubstr s = "12345678"; + t_(s, 12345678); + t_(s.first(8), 12345678); + t_(s.first(7), 1234567); + t_(s.first(6), 123456); + t_(s.first(5), 12345); + t_(s.first(4), 1234); + t_(s.first(3), 123); + t_(s.first(2), 12); + t_(s.first(1), 1); +} + +TEST_CASE_TEMPLATE("atof.hexa", T, float, double) +{ + auto t_ = [](csubstr str, bool isok){ + T rval = {}; + INFO("str=" << str); + CHECK_EQ(atox(str, &rval), isok); + }; + #if C4CORE_NO_FAST_FLOAT + #define _scanf_accepts(expected) !expected + #else + #define _scanf_accepts(expected) expected + #endif + t_("0x1.p+0", true); + t_("0x1.p", _scanf_accepts(false)); + t_("0x1.p+", _scanf_accepts(false)); + t_("0x12p+0", true); + t_("0x12p", _scanf_accepts(false)); + t_("0xabcdef.abcdefp+0", true); + t_("0xABCDEF.ABCDEFp+0", true); + t_("0x1g", _scanf_accepts(false)); + t_("0x1.2", true); + t_("0x1.", true); + t_("0x1.0329p+0", true); + t_("0x1.0329P+0", true); + t_("0x1.aAaAaAp+0", true); + t_("0x1.agA+0", _scanf_accepts(false)); +} + +TEST_CASE_TEMPLATE("atof.infnan", T, float, double) +{ + T pinf = std::numeric_limits<T>::infinity(); + T ninf = -std::numeric_limits<T>::infinity(); + T nan = std::numeric_limits<T>::quiet_NaN(); + T rval = {}; + test_ator("infinity", pinf); + test_ator("inf", pinf); + test_ator("-infinity", ninf); + test_ator("-inf", ninf); + test_ator("nan", nan); +} + +TEST_CASE_TEMPLATE("atof.fail_parse", T, float, double) +{ + auto t_ = [](csubstr str){ + T rval; + INFO("str=" << str << " rval=" << rval); + CHECK_EQ(atox(str, &rval), false); + }; + t_(".inf"); + t_("-.inf"); + t_(".nan"); + t_("-.nan"); + t_("not a float!"); + #ifndef C4CORE_NO_FAST_FLOAT + t_("0xfonix!"); + #endif + //t_("123.45not a float!"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +TEST_CASE_TEMPLATE("to_chars.empty_buffer", T, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, void*) +{ + char buf_[100]; + substr buf = buf_; + CHECK_EQ(to_chars({}, T(101)), to_chars(buf_, T(101))); + CHECK_EQ(to_chars({}, T(101)), to_chars(buf , T(101))); +} +// due to an implementation quirk with sprintf, for floats the empty is GE +TEST_CASE_TEMPLATE("to_chars.empty_buffer", T, float, double) +{ + char buf_[100]; + substr buf = buf_; + CHECK_GE(to_chars({}, T(101)), to_chars(buf_, T(101))); + CHECK_GE(to_chars({}, T(101)), to_chars(buf , T(101))); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE("to_chars.std_string") +{ + std::string foo("foo"); + char buf_[32]; + substr buf(buf_); + size_t result = to_chars(buf, foo); + CHECK_EQ(result, 3); + CHECK_EQ(buf.first(3), "foo"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE("to_chars.bool") +{ + char buf_[32]; + substr buf(buf_); + csubstr result = to_chars_sub(buf, true); + CHECK_EQ(result, "1"); + result = to_chars_sub(buf, false); + CHECK_EQ(result, "0"); +} + +TEST_CASE("from_chars.bool") +{ + bool result = false; + for(const char *s : {"1", "true", "True", "TRUE"}) + { + INFO("s='" << s << "'"); + bool ok = from_chars(to_csubstr(s), &result); + CHECK_UNARY(ok); + CHECK_UNARY(result); + } + for(const char *s : {"0", "false", "False", "FALSE"}) + { + INFO("s='" << s << "'"); + bool ok = from_chars(to_csubstr(s), &result); + CHECK_UNARY(ok); + CHECK_UNARY_FALSE(result); + } +} + +TEST_CASE("from_chars_first.bool") +{ + bool result = false; + for(const char *s : {"1", "10000", "2", "3", "10", "010", "001", "0001", "true", "True", "TRUE"}) + { + INFO("s='" << s << "'"); + bool ok = from_chars(to_csubstr(s), &result); + CHECK_UNARY(ok); + CHECK_UNARY(result); + } + for(const char *s : {"0", "00", "000", "0000", "false", "False", "FALSE"}) + { + INFO("s='" << s << "'"); + bool ok = from_chars(to_csubstr(s), &result); + CHECK_UNARY(ok); + CHECK_UNARY_FALSE(result); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +// test that no characters are trimmed at the end of +// the number due to printf-based implementations +// needing space for the \0 +template<class T> +void test_trimmed_fit(T v, csubstr expected) +{ + char buf_[128] = {}; + char buf2_[128] = {}; + substr buf(buf_); + substr buf2(buf_); + REQUIRE_GE(buf.len, expected.len); + REQUIRE_GE(buf2.len, expected.len); + csubstr result = to_chars_sub(buf, v); + CHECK_EQ(result, expected); + csubstr result2 = to_chars_sub(buf2.sub(result.len), v); + CHECK_EQ(result2, result); + std::string str; + catrs(&str, v); + CHECK_EQ(expected, to_csubstr(str)); + CHECK_EQ(result, to_csubstr(str)); +} + +TEST_CASE("to_chars.trimmed_fit_int") +{ + test_trimmed_fit(12345678, "12345678"); +} + +TEST_CASE("to_chars.trimmed_fit_float") +{ + test_trimmed_fit(0.374f, "0.374"); + test_trimmed_fit(12.374f, "12.374"); +} + +TEST_CASE("to_chars.trimmed_fit_double") +{ + test_trimmed_fit(0.374, "0.374"); + test_trimmed_fit(12.374, "12.374"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T> +void to_chars_roundtrip(substr buf, T const& val, csubstr expected) +{ + T cp = {}; + INFO("val=" << val); + csubstr res = to_chars_sub(buf, val); + CHECK_EQ(res, expected); + bool ok = from_chars(res, &cp); + CHECK_UNARY(ok); + CHECK_EQ(cp, val); +} + +template<size_t N> +void to_chars_roundtrip(char (&buf)[N], csubstr val) +{ + char cp_[N] = {}; + substr cp = cp_; + INFO("val=" << val); + REQUIRE_LE(val.len, N); + csubstr res = to_chars_sub(buf, val); + CHECK_EQ(res.len, val.len); + CHECK_EQ(res, val); + bool ok = from_chars(res, &cp); + CHECK_UNARY(ok); + CHECK_EQ(cp, val); +} + + +TEST_CASE("to_chars.roundtrip_bool") +{ + char buf[128]; + to_chars_roundtrip<bool>(buf, false, "0"); + to_chars_roundtrip<bool>(buf, true, "1"); +} + + +TEST_CASE("to_chars.roundtrip_char") +{ + char buf[128]; + to_chars_roundtrip<char>(buf, 'a', "a"); + to_chars_roundtrip<char>(buf, 'b', "b"); + to_chars_roundtrip<char>(buf, 'c', "c"); + to_chars_roundtrip<char>(buf, 'd', "d"); +} + +#define C4_TEST_ROUNDTRIP_INT(ty) \ +TEST_CASE("to_chars.roundtrip_" #ty)\ +{\ + char buf[128];\ + to_chars_roundtrip<ty>(buf, 0, "0");\ + to_chars_roundtrip<ty>(buf, 1, "1");\ + to_chars_roundtrip<ty>(buf, 2, "2");\ + to_chars_roundtrip<ty>(buf, 3, "3");\ + to_chars_roundtrip<ty>(buf, 4, "4");\ +} +C4_TEST_ROUNDTRIP_INT(int8_t) +C4_TEST_ROUNDTRIP_INT(int16_t) +C4_TEST_ROUNDTRIP_INT(int32_t) +C4_TEST_ROUNDTRIP_INT(int64_t) +C4_TEST_ROUNDTRIP_INT(uint8_t) +C4_TEST_ROUNDTRIP_INT(uint16_t) +C4_TEST_ROUNDTRIP_INT(uint32_t) +C4_TEST_ROUNDTRIP_INT(uint64_t) +// some of the following types are not the same as above: +using ulong = unsigned long; +using uint = unsigned int; +C4_TEST_ROUNDTRIP_INT(int) +C4_TEST_ROUNDTRIP_INT(uint) +C4_TEST_ROUNDTRIP_INT(long) +C4_TEST_ROUNDTRIP_INT(ulong) +C4_TEST_ROUNDTRIP_INT(size_t) +C4_TEST_ROUNDTRIP_INT(intptr_t) +C4_TEST_ROUNDTRIP_INT(uintptr_t) + +#define C4_TEST_ROUNDTRIP_REAL(ty) \ +TEST_CASE("to_chars.roundtrip_" #ty)\ +{\ + char buf[128];\ + to_chars_roundtrip<ty>(buf, ty(0.0), "0");\ + to_chars_roundtrip<ty>(buf, ty(1.0), "1");\ + to_chars_roundtrip<ty>(buf, ty(2.0), "2");\ + to_chars_roundtrip<ty>(buf, ty(3.0), "3");\ + to_chars_roundtrip<ty>(buf, ty(4.0), "4");\ +} +C4_TEST_ROUNDTRIP_REAL(float) +C4_TEST_ROUNDTRIP_REAL(double) + +TEST_CASE("to_chars.roundtrip_substr") +{ + char buf[128]; + to_chars_roundtrip(buf, ""); + to_chars_roundtrip(buf, "0"); + to_chars_roundtrip(buf, "1"); + to_chars_roundtrip(buf, "2"); + to_chars_roundtrip(buf, "3"); + to_chars_roundtrip(buf, "4"); + to_chars_roundtrip(buf, "zhis iz a test"); +} + +TEST_CASE("to_chars.substr_enough_size") +{ + char orig_[] = "0123456789"; + substr orig = orig_; + char result_[20]; + substr result = result_; + size_t len = to_chars(result, orig); + CHECK_EQ(len, orig.len); + CHECK_NE(result.str, orig.str); + CHECK_EQ(result.first(10), orig); +} + +TEST_CASE("to_chars.substr_insufficient_size") +{ + char orig_[] = "0123456789"; + substr orig = orig_; + char result_[11] = {}; + substr result = result_; + result.len = 5; + size_t len = to_chars(result, orig); + CHECK_EQ(len, orig.len); + CHECK_NE(result.str, orig.str); + CHECK_EQ(result.first(5), "01234"); + CHECK_EQ(substr(result_).last(5), "\0\0\0\0\0"); +} + +TEST_CASE("from_chars.csubstr") +{ + csubstr orig = "0123456789"; + csubstr result; + CHECK_NE(result.str, orig.str); + CHECK_NE(result.len, orig.len); + bool ok = from_chars(orig, &result); + CHECK(ok); + CHECK_EQ(result.str, orig.str); + CHECK_EQ(result.len, orig.len); +} + +TEST_CASE("from_chars.substr_enough_size") +{ + char buf_[128] = {}; + substr result = buf_; + for(char r : result) + { + CHECK_EQ(r, '\0'); + } + bool ok = from_chars("0123456789", &result); + CHECK(ok); + CHECK_EQ(result.len, 10); + CHECK_EQ(result.str, buf_); + CHECK_EQ(result, "0123456789"); +} + +TEST_CASE("from_chars.substr_insufficient_size") +{ + char buf_[128] = {}; + substr buf = buf_; + buf.len = 0; + bool ok = from_chars("0123456789", &buf); + CHECK_FALSE(ok); + for(size_t i = 0; i < 10; ++i) + { + CHECK_EQ(buf_[i], '\0'); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +struct ptr_expected { void *ptr; c4::csubstr str; }; +const ptr_expected ptr_cases[] = { + {(void*)0x0, c4::csubstr("0x0")}, + {(void*)0x1234, c4::csubstr("0x1234")}, + {(void*)-0x1234, c4::csubstr("-0x1234")}, +}; + +template<class T> +void test_xtoa_ptr(const char *type_name) +{ + INFO("type=" << type_name); + char buf_[128] = {}; + c4::substr buf(buf_); + for(auto &pe : ptr_cases) + { + INFO("val=" << pe.str); + size_t ret = xtoa(buf, (T const*)pe.ptr); + CHECK_EQ(ret, pe.str.len); + CHECK_EQ(buf.first(ret), pe.str); + } +} + +template<class T> +void test_to_chars_ptr(const char *type_name) +{ + INFO("type=" << type_name); + char buf_[128] = {}; + c4::substr buf(buf_); + for(auto &pe : ptr_cases) + { + INFO("val=" << pe.str); + size_t ret = to_chars(buf, (T const*)pe.ptr); + CHECK_EQ(ret, pe.str.len); + CHECK_EQ(buf.first(ret), pe.str); + } +} + +template<class T> +void test_atox_ptr(const char *type_name) +{ + INFO("type=" << type_name); + for(auto &pe : ptr_cases) + { + T *ptr = nullptr; + INFO("val=" << pe.str); + bool ret = atox(pe.str, &ptr); + CHECK(ret); + CHECK_EQ((void*)ptr, pe.ptr); + } +} + +template<class T> +void test_from_chars_ptr(const char *type_name) +{ + INFO("type=" << type_name); + for(auto &pe : ptr_cases) + { + T *ptr = nullptr; + INFO("val=" << pe.str); + bool ret = from_chars(pe.str, &ptr); + CHECK(ret); + CHECK_EQ((void*)ptr, pe.ptr); + } +} + +template<class T> +void test_from_chars_first_ptr(const char *type_name) +{ + INFO("type=" << type_name); + for(auto &pe : ptr_cases) + { + T *ptr = nullptr; + INFO("val=" << pe.str); + bool ret = from_chars(pe.str, &ptr); + CHECK(ret); + CHECK_EQ((void*)ptr, pe.ptr); + } +} + + +TEST_CASE("xtoa.ptr") +{ + test_xtoa_ptr<void>("void"); + test_xtoa_ptr<int>("int"); + test_xtoa_ptr<std::vector<int>>("std::vector<int>"); +} + +TEST_CASE("atox.ptr") +{ + test_atox_ptr<void>("void"); + test_atox_ptr<int>("int"); + test_atox_ptr<std::vector<int>>("std::vector<int>"); +} + +TEST_CASE("to_chars.ptr") +{ + test_to_chars_ptr<void>("void"); + test_to_chars_ptr<int>("int"); + test_to_chars_ptr<std::vector<int>>("std::vector<int>"); +} + +TEST_CASE("from_chars.ptr") +{ + test_from_chars_ptr<void>("void"); + test_from_chars_ptr<int>("int"); + test_from_chars_ptr<std::vector<int>>("std::vector<int>"); +} + +TEST_CASE("from_chars_first.ptr") +{ + test_from_chars_first_ptr<void>("void"); + test_from_chars_first_ptr<int>("int"); + test_from_chars_first_ptr<std::vector<int>>("std::vector<int>"); +} + +} // namespace c4 + + +C4_SUPPRESS_WARNING_GCC_POP +C4_SUPPRESS_WARNING_CLANG_POP + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_ctor_dtor.cpp b/thirdparty/ryml/ext/c4core/test/test_ctor_dtor.cpp new file mode 100644 index 000000000..f655fb17c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_ctor_dtor.cpp @@ -0,0 +1,306 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/ctor_dtor.hpp" +#endif + +#include "c4/libtest/supprwarn_push.hpp" + +#include <c4/test.hpp> +#include <string> +#include <vector> + +namespace c4 { + +namespace { +struct subject +{ + static size_t ct_cp, ct_mv, cp, mv; + static void clear() { ct_cp = ct_mv = cp = mv = 0; } + subject(Counting<std::string> const&) + { + ++ct_cp; + } + subject(Counting<std::string> &&) + { + ++ct_mv; + } + subject(subject const&) + { + ++cp; + } + subject(subject &&) + { + ++mv; + } +}; +size_t subject::ct_cp = 0; +size_t subject::ct_mv = 0; +size_t subject::cp = 0; +size_t subject::mv = 0; +} // empty namespace + + +TEST_CASE("ctor_dtor.construct_n") +{ + using T = Counting<subject>; + C4_STATIC_ASSERT(sizeof(T) % alignof(T) == 0); + alignas(T) char buf1[100 * sizeof(T)]; + T* mem1 = reinterpret_cast<T*>(buf1); + + using cs = Counting<std::string>; + + decltype(subject::ct_cp) num = 10; + + { + auto chc = T::check_num_ctors_dtors(num, 0); + auto ch = cs::check_num_ctors_dtors(1, 1); + cs s("bla"); + construct_n(mem1, num, s); + CHECK_EQ(subject::ct_cp, num); + subject::clear(); + } + + { + auto chc = T::check_num_ctors_dtors(num, 0); + auto ch = cs::check_num_ctors_dtors(1, 1); + construct_n(mem1, num, cs("bla")); // BAD!!! will call 10 moves + CHECK_EQ(subject::ct_cp, num); + subject::clear(); + } +} + + +//----------------------------------------------------------------------------- +template<class T> +void create_make_room_buffer(std::vector<T> &orig) +{ + C4_STATIC_ASSERT(std::is_integral<T>::value); + for(int i = 0, e = (int)orig.size(); i < e; ++i) + { + orig[static_cast<size_t>(i)] = (T)(33 + i % (122 - 33)); // assign characters + } +} +template<> +void create_make_room_buffer<std::string>(std::vector<std::string> &orig) +{ + for(int i = 0, e = (int)orig.size(); i < e; ++i) + { + char c = (char)(33 + i % (122 - 33)); + orig[static_cast<size_t>(i)].assign(10, c); + } +} + +template<class T> +void do_make_room_inplace(std::vector<T> const& orig, std::vector<T> & buf, + size_t bufsz, size_t room, size_t pos) +{ + buf = orig; + make_room(buf.data() + pos, bufsz, room); +} + +template<class T> +void do_make_room_srcdst(std::vector<T> const& orig, std::vector<T> & buf, + size_t bufsz, size_t room, size_t pos) +{ + buf.resize(orig.size()); + for(auto &t : buf) + { + t = T(); + } + make_room(buf.data(), orig.data(), bufsz, room, pos); +} + +template<class T> +void do_make_room_check(std::vector<T> const& orig, std::vector<T> & buf, + size_t bufsz, size_t room, size_t pos) +{ + for(size_t i = 0, e = orig.size(); i < e; ++i) + { + INFO("i=" << (int)i); + if(i < pos) + { + // memory before the move, must be untouched + CHECK_EQ(buf[i], orig[i]); + } + else + { + if(i >= pos && i < pos + room) + { + // this is the memory that was moved (at its origin) + //CHECK_EQ(buf[i], orig[i]) << "i=" << (int)i; + } + else if(i >= pos + room && i < pos + room + bufsz) + { + // this is the memory that was moved (at its destination) + CHECK_EQ(buf[i], orig[i - room]); + } + else + { + // this is memory at the end, must be untouched + CHECK_EQ(buf[i], orig[i]); + } + } + } +}; + +template<class T> +void do_make_room_inplace_test(std::vector<T> const& orig, std::vector<T> & buf, + size_t bufsz, size_t room, size_t pos) +{ + do_make_room_inplace(orig, buf, bufsz, room, pos); + do_make_room_check(orig, buf, bufsz, room, pos); +} + +template<class T> +void do_make_room_srcdst_test(std::vector<T> const& orig, std::vector<T> & buf, + size_t /*bufsz*/, size_t room, size_t pos) +{ + do_make_room_srcdst(orig, buf, buf.size() - room, room, pos); + do_make_room_check(orig, buf, buf.size() - room, room, pos); +} + +template<class T, class Func> +void test_make_room(Func test_func) +{ + std::vector<T> orig(100), buf(100); + + create_make_room_buffer(orig); + + { + INFO("in the beginning without overlap"); + test_func(orig, buf, /*bufsz*/10, /*room*/10, /*pos*/0); + } + + { + INFO("in the beginning with overlap"); + test_func(orig, buf, /*bufsz*/10, /*room*/15, /*pos*/0); + } + + { + INFO("in the middle without overlap"); + test_func(orig, buf, /*bufsz*/10, /*room*/10, /*pos*/10); + } + + { + INFO("in the middle with overlap"); + test_func(orig, buf, /*bufsz*/10, /*room*/15, /*pos*/10); + } +} + +TEST_CASE_TEMPLATE("ctor_dtor.make_room_inplace", T, uint8_t, uint64_t, std::string) +{ + test_make_room<T>(do_make_room_inplace_test<T>); +} + +TEST_CASE_TEMPLATE("ctor_dtor.make_room_srcdst", T, uint8_t, uint64_t, std::string) +{ + test_make_room<T>(&do_make_room_srcdst_test<T>); +} + + +//----------------------------------------------------------------------------- + +template<class T> +void do_destroy_room_inplace(std::vector<T> const& orig, std::vector<T> & buf, + size_t bufsz, size_t room, size_t pos) +{ + buf = orig; + destroy_room(buf.data() + pos, bufsz - pos, room); +} + +template<class T> +void do_destroy_room_srcdst(std::vector<T> const& orig, std::vector<T> & buf, + size_t bufsz, size_t room, size_t pos) +{ + buf = orig; + destroy_room(buf.data(), orig.data(), bufsz, room, pos); +} + +template<class T> +void do_destroy_room_check(std::vector<T> const& orig, std::vector<T> & buf, + size_t bufsz, size_t room, size_t pos) +{ + for(size_t i = 0, e = orig.size(); i < e; ++i) + { + INFO("i=" << (int)i << " room=" << room << " pos=" << pos); + if(i < pos) + { + // memory before the destroy, should be untouched + CHECK_EQ(buf[i], orig[i]); + } + else + { + if(i >= pos && i < pos + room) + { + // this is the memory that was destroyed (at its origin) + } + else if(i >= pos + room && i < pos + room + bufsz) + { + // this is the memory that was moved (at its destination) + CHECK_EQ(buf[i - room], orig[i]); + } + else + { + // this is memory at the end, should be untouched + CHECK_EQ(buf[i], orig[i]); + } + } + } +}; + +template<class T> +void do_destroy_room_inplace_test(std::vector<T> const& orig, std::vector<T> & buf, + size_t room, size_t pos) +{ + do_destroy_room_inplace(orig, buf, buf.size(), room, pos); + do_destroy_room_check(orig, buf, buf.size(), room, pos); +} + +template<class T> +void do_destroy_room_srcdst_test(std::vector<T> const& orig, std::vector<T> & buf, + size_t room, size_t pos) +{ + do_destroy_room_srcdst(orig, buf, buf.size(), room, pos); + do_destroy_room_check(orig, buf, buf.size(), room, pos); +} + +template<class T, class Func> +void test_destroy_room(Func test_func) +{ + std::vector<T> orig(100), buf(100); + + create_make_room_buffer(orig); + + { + INFO("in the beginning, room=10"); + test_func(orig, buf, /*room*/10, /*pos*/0); + } + + { + INFO("in the beginning, room=20"); + test_func(orig, buf, /*room*/20, /*pos*/0); + } + + { + INFO("in the middle, room=10"); + test_func(orig, buf, /*room*/10, /*pos*/10); + } + + { + INFO("in the middle, room=20"); + test_func(orig, buf, /*room*/20, /*pos*/10); + } +} + +TEST_CASE_TEMPLATE("ctor_dtor.destroy_room_inplace", T, uint8_t, uint64_t, std::string) +{ + test_destroy_room<T>(do_destroy_room_inplace_test<T>); +} + +TEST_CASE_TEMPLATE("ctor_dtor.destroy_room_srcdst", T, uint8_t, uint64_t, std::string) +{ + test_destroy_room<T>(&do_destroy_room_srcdst_test<T>); +} + +} // namespace c4 + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_dump.cpp b/thirdparty/ryml/ext/c4core/test/test_dump.cpp new file mode 100644 index 000000000..cfa7d991c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_dump.cpp @@ -0,0 +1,1220 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/substr.hpp" +#include "c4/std/std.hpp" +#include "c4/dump.hpp" +#include "c4/format.hpp" +#endif + +#include <c4/test.hpp> +#include "c4/libtest/supprwarn_push.hpp" + +#ifdef __clang__ +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif + +namespace c4 { + + +namespace example { + +std::string test_dumper_target = {}; +void test_dumper(csubstr str) +{ + test_dumper_target.append(str.str, str.len); +} + +template<class ...Args> +void printf(csubstr fmt, Args&& ...args) +{ + static thread_local std::string writebuf(16, '\0'); + DumpResults results = format_dump_resume<&test_dumper>(c4::to_substr(writebuf), fmt, std::forward<Args>(args)...); + if(C4_UNLIKELY(results.bufsize > writebuf.size())) // bufsize will be that of the largest element serialized. Eg int(1), will require 1 byte. + { + size_t dup = 2 * writebuf.size(); + writebuf.resize(dup > results.bufsize ? dup : results.bufsize); + format_dump_resume<&test_dumper>(results, c4::to_substr(writebuf), fmt, std::forward<Args>(args)...); + } +} +} // namespace example + +TEST_CASE("printf_example") +{ + example::test_dumper_target.clear(); + SUBCASE("1") + { + example::printf("{} coffees per day.\n", 3); + CHECK_EQ(example::test_dumper_target, "3 coffees per day.\n"); + } + SUBCASE("2") + { + example::printf("{} would be {}.", "brecky", "nice"); + CHECK_EQ(example::test_dumper_target, "brecky would be nice."); + } + SUBCASE("resize writebuf") + { + // printed strings will not use the writebuf, so we write a zero-padded integer + size_t dim = 128; // pad with 128 zeroes + std::string s1(dim, '0'); + std::string s2(dim, '0'); + s1.back() = '1'; + s2.back() = '2'; + example::printf("{} cannot be {}", fmt::zpad(1, dim), fmt::zpad(2, dim)); + CHECK_EQ(example::test_dumper_target, s1 + " cannot be " + s2); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE("DumpResults") +{ + DumpResults dr = {}; + CHECK_EQ(dr.bufsize, 0u); + CHECK_EQ(dr.lastok, DumpResults::noarg); + CHECK_UNARY(dr.write_arg(0)); + CHECK_FALSE(dr.success_until(0)); + CHECK_EQ(dr.argfail(), 0); +} + +struct DumpChecker +{ + static size_t s_num_calls; + static char s_workspace[100]; + static size_t s_accum_pos; + static char s_accum[100]; + static void s_reset() + { + s_num_calls = 0; + s_accum_pos = 0; + for(size_t i = 0; i < sizeof(s_workspace); ++i) + s_workspace[i] = '+'; + for(size_t i = 0; i < sizeof(s_accum); ++i) + s_accum[i] = '.'; + } + static void s_dump(csubstr buf) + { + REQUIRE_LT(buf.len, sizeof(s_workspace)); + REQUIRE_LT(s_accum_pos + buf.len, sizeof(s_accum)); + ++s_num_calls; + memcpy(s_accum + s_accum_pos, buf.str, buf.len); + s_accum_pos += buf.len; + } +}; +size_t DumpChecker::s_num_calls = 0; +char DumpChecker::s_workspace[100] = {}; +size_t DumpChecker::s_accum_pos = {}; +char DumpChecker::s_accum[100] = {}; + +struct CatDumpTplArg +{ + template<class ...Args> + static size_t call_cat_dump(Args&& ...args) + { + return cat_dump<&DumpChecker::s_dump>(std::forward<Args>(args)...); + } + template<class ...Args> + static DumpResults call_cat_dump_resume(Args&& ...args) + { + return cat_dump_resume<&DumpChecker::s_dump>(std::forward<Args>(args)...); + } + template<class ...Args> + static size_t call_catsep_dump(Args&& ...args) + { + return catsep_dump<&DumpChecker::s_dump>(std::forward<Args>(args)...); + } + template<class ...Args> + static DumpResults call_catsep_dump_resume(Args&& ...args) + { + return catsep_dump_resume<&DumpChecker::s_dump>(std::forward<Args>(args)...); + } + template<class ...Args> + static size_t call_format_dump(Args&& ...args) + { + return format_dump<&DumpChecker::s_dump>(std::forward<Args>(args)...); + } + template<class ...Args> + static DumpResults call_format_dump_resume(Args&& ...args) + { + return format_dump_resume<&DumpChecker::s_dump>(std::forward<Args>(args)...); + } +}; + +struct CatDumpFnArg +{ + template<class ...Args> + static size_t call_cat_dump(Args&& ...args) + { + return cat_dump(&DumpChecker::s_dump, std::forward<Args>(args)...); + } + template<class ...Args> + static DumpResults call_cat_dump_resume(Args&& ...args) + { + return cat_dump_resume(&DumpChecker::s_dump, std::forward<Args>(args)...); + } + template<class ...Args> + static size_t call_catsep_dump(Args&& ...args) + { + return catsep_dump(&DumpChecker::s_dump, std::forward<Args>(args)...); + } + template<class ...Args> + static DumpResults call_catsep_dump_resume(Args&& ...args) + { + return catsep_dump_resume(&DumpChecker::s_dump, std::forward<Args>(args)...); + } + template<class ...Args> + static size_t call_format_dump(Args&& ...args) + { + return format_dump(&DumpChecker::s_dump, std::forward<Args>(args)...); + } + template<class ...Args> + static DumpResults call_format_dump_resume(Args&& ...args) + { + return format_dump_resume(&DumpChecker::s_dump, std::forward<Args>(args)...); + } +}; + +namespace buffers { +int b1 = 1; +int b2 = 22; +int b3 = 333; +int b4 = 4444; +int sep = 90009; +size_t seplen = 5; +} + +TEST_CASE_TEMPLATE("cat_dump", T, CatDumpTplArg, CatDumpFnArg) +{ + using namespace buffers; + substr buf = DumpChecker::s_workspace; + auto accum = [&]{ return csubstr(DumpChecker::s_accum).first(DumpChecker::s_accum_pos); }; + SUBCASE("dont use the buffer with strings") + { + DumpChecker::s_reset(); + size_t needed_size = T::call_cat_dump(buf.first(0), b1); + CHECK_EQ(needed_size, 1); + CHECK_EQ(accum(), csubstr("")); + needed_size = T::call_cat_dump(buf.first(0), b1, b2); + CHECK_EQ(needed_size, 2); + CHECK_EQ(accum(), csubstr("")); + needed_size = T::call_cat_dump(buf.first(0), b1, b2, b3); + CHECK_EQ(needed_size, 3); + CHECK_EQ(accum(), csubstr("")); + needed_size = T::call_cat_dump(buf.first(0), b1, b2, b3, b4); + CHECK_EQ(needed_size, 4); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(0), ("1")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("")); + needed_size = T::call_cat_dump(buf.first(0), ("1"), ("22")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("")); + needed_size = T::call_cat_dump(buf.first(0), ("1"), ("22"), ("333")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("")); + needed_size = T::call_cat_dump(buf.first(0), ("1"), ("22"), ("333"), ("4444")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(0), csubstr("1")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("")); + needed_size = T::call_cat_dump(buf.first(0), csubstr("1"), csubstr("22")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("")); + needed_size = T::call_cat_dump(buf.first(0), csubstr("1"), csubstr("22"), csubstr("333")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("")); + needed_size = T::call_cat_dump(buf.first(0), csubstr("1"), csubstr("22"), csubstr("333"), csubstr("4444")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(1), b1); + CHECK_EQ(needed_size, 1); + CHECK_EQ(accum(), csubstr("1")); + needed_size = T::call_cat_dump(buf.first(1), b1, b2); + CHECK_EQ(needed_size, 2); + CHECK_EQ(accum(), csubstr("11")); + needed_size = T::call_cat_dump(buf.first(1), b1, b2, b3); + CHECK_EQ(needed_size, 3); + CHECK_EQ(accum(), csubstr("111")); + needed_size = T::call_cat_dump(buf.first(1), b1, b2, b3, b4); + CHECK_EQ(needed_size, 4); + CHECK_EQ(accum(), csubstr("1111")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(1), ("1")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("1")); + needed_size = T::call_cat_dump(buf.first(1), ("1"), ("22")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("1122")); + needed_size = T::call_cat_dump(buf.first(1), ("1"), ("22"), ("333")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("1122122333")); + needed_size = T::call_cat_dump(buf.first(1), ("1"), ("22"), ("333"), ("4444")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("11221223331223334444")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(1), csubstr("1")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("1")); + needed_size = T::call_cat_dump(buf.first(1), csubstr("1"), csubstr("22")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("1122")); + needed_size = T::call_cat_dump(buf.first(1), csubstr("1"), csubstr("22"), csubstr("333")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("1122122333")); + needed_size = T::call_cat_dump(buf.first(1), csubstr("1"), csubstr("22"), csubstr("333"), csubstr("4444")); + CHECK_EQ(needed_size, 0); + CHECK_EQ(accum(), csubstr("11221223331223334444")); + } + SUBCASE("1") + { + DumpChecker::s_reset(); + size_t needed_size = T::call_cat_dump(buf.first(0), b1); + CHECK_EQ(needed_size, 1u); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(1), csubstr("+")); // nothing was written + CHECK_EQ(accum(), csubstr("")); // nothing was written + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf, b1); + CHECK_EQ(needed_size, 1u); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(1), csubstr("1")); + CHECK_EQ(accum(), csubstr("1")); + } + SUBCASE("1 2") + { + DumpChecker::s_reset(); + size_t needed_size = T::call_cat_dump(buf.first(0), b1, b2); + CHECK_EQ(needed_size, 2); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(2), csubstr("++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(1), b1, b2); + CHECK_EQ(needed_size, 2); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(2), csubstr("1+")); // only the first character of b2 was written + CHECK_EQ(accum(), csubstr("1")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf, b1, b2); + CHECK_EQ(needed_size, 2); + CHECK_EQ(DumpChecker::s_num_calls, 2); + CHECK_EQ(buf.first(2), csubstr("22")); + CHECK_EQ(accum(), csubstr("122")); + } + SUBCASE("2 1") + { + DumpChecker::s_reset(); + size_t needed_size = T::call_cat_dump(buf, b2, b1); + CHECK_EQ(needed_size, 2); + CHECK_EQ(DumpChecker::s_num_calls, 2); + CHECK_EQ(buf.first(2), csubstr("12")); // wrote 2 then 1 + CHECK_EQ(accum(), csubstr("221")); + } + SUBCASE("1 2 3 4") + { + DumpChecker::s_reset(); + size_t needed_size = T::call_cat_dump(buf.first(0), b1, b2, b3, b4); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(1), b1, b2, b3, b4); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); + CHECK_EQ(accum(), csubstr("1")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(2), b1, b2, b3, b4); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 2); + CHECK_EQ(buf.first(4), csubstr("22++")); + CHECK_EQ(accum(), csubstr("122")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(3), b1, b2, b3, b4); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(4), csubstr("333+")); + CHECK_EQ(accum(), csubstr("122333")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf, b1, b2, b3, b4); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 4); + CHECK_EQ(buf.first(4), csubstr("4444")); + CHECK_EQ(accum(), csubstr("1223334444")); + } + SUBCASE("4 3 2 1") + { + DumpChecker::s_reset(); + size_t needed_size = T::call_cat_dump(buf.first(0), b4, b3, b2, b1); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(1), b4, b3, b2, b1); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(2), b4, b3, b2, b1); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf.first(3), b4, b3, b2, b1); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_cat_dump(buf, b4, b3, b2, b1); + CHECK_EQ(needed_size, 4); + CHECK_EQ(DumpChecker::s_num_calls, 4); + CHECK_EQ(buf.first(4), csubstr("1234")); + CHECK_EQ(accum(), csubstr("4444333221")); + } +} + +TEST_CASE_TEMPLATE("cat_dump_resume", T, CatDumpTplArg, CatDumpFnArg) +{ + using namespace buffers; + substr buf = DumpChecker::s_workspace; + auto accum = [&]{ return csubstr(DumpChecker::s_accum).first(DumpChecker::s_accum_pos); }; + SUBCASE("1") + { + DumpChecker::s_reset(); + DumpResults ret = T::call_cat_dump_resume(buf.first(0), b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_EQ(ret.bufsize, 1); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(1), csubstr("+")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + DumpResults retry = T::call_cat_dump_resume(ret, buf.first(1), b1); + CHECK_UNARY(retry.success_until(0)); + CHECK_UNARY(!retry.success_until(1)); + CHECK_UNARY(!retry.success_until(2)); + CHECK_EQ(retry.bufsize, 1); + CHECK_EQ(retry.lastok, 0); + CHECK_EQ(retry.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(1), csubstr("1")); + CHECK_EQ(accum(), csubstr("1")); + } + SUBCASE("1 2") + { + DumpChecker::s_reset(); + DumpResults ret = T::call_cat_dump_resume(buf.first(0), b1, b2); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_EQ(ret.bufsize, 2); // finds the buf size at once + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(2), csubstr("++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(1), b1, b2); + CHECK_UNARY(!ret.success_until(0)); // ret.bufsize signals buffer is at least 2 + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_EQ(ret.bufsize, 2); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(2), csubstr("++")); + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(2), b1, b2); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_EQ(ret.bufsize, 2); + CHECK_EQ(ret.lastok, 1); + CHECK_EQ(ret.argfail(), 2); + CHECK_EQ(DumpChecker::s_num_calls, 2); + CHECK_EQ(buf.first(2), csubstr("22")); + CHECK_EQ(accum(), csubstr("122")); + } + SUBCASE("1 2 3 4") + { + DumpChecker::s_reset(); + DumpResults ret = T::call_cat_dump_resume(buf.first(0), b1, b2, b3, b4); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_UNARY(!ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(4), csubstr("++++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(1), b1, b2, b3, b4); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_UNARY(!ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(2), b1, b2, b3, b4); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_UNARY(!ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(3), b1, b2, b3, b4); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_UNARY(!ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(4), b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_UNARY(ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, 3); + CHECK_EQ(ret.argfail(), 4); + CHECK_EQ(DumpChecker::s_num_calls, 4); + CHECK_EQ(buf.first(4), csubstr("4444")); + CHECK_EQ(accum(), csubstr("1223334444")); + } + SUBCASE("4 3 2 1") + { + DumpChecker::s_reset(); + DumpResults ret = T::call_cat_dump_resume(buf.first(0), b4, b3, b2, b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_UNARY(!ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(4), csubstr("++++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(1), b4, b3, b2, b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_UNARY(!ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(2), b4, b3, b2, b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_UNARY(!ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(3), b4, b3, b2, b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_UNARY(!ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + ret = T::call_cat_dump_resume(ret, buf.first(4), b4, b3, b2, b1); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_UNARY(ret.success_until(3)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, 3); + CHECK_EQ(ret.argfail(), 4); + CHECK_EQ(DumpChecker::s_num_calls, 4); + CHECK_EQ(buf.first(4), csubstr("1234")); + CHECK_EQ(accum(), csubstr("4444333221")); + } +} + + + +TEST_CASE_TEMPLATE("catsep_dump", T, CatDumpTplArg, CatDumpFnArg) +{ + using namespace buffers; + size_t needed_size; + substr buf = DumpChecker::s_workspace; + auto accum = [&]{ return csubstr(DumpChecker::s_accum).first(DumpChecker::s_accum_pos); }; + SUBCASE("1") + { + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(0), sep, b1); + CHECK_EQ(needed_size, 1); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(1), csubstr("+")); // nothing was written + CHECK_EQ(accum(), csubstr("")); // nothing was written + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(1), sep, b1); + CHECK_EQ(needed_size, 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(1), csubstr("1")); + CHECK_EQ(accum(), csubstr("1")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(1 + seplen), sep, b1); + CHECK_EQ(needed_size, 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); // sep was not written + CHECK_EQ(buf.first(1), csubstr("1")); + CHECK_EQ(accum(), csubstr("1")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf, sep, b1); + CHECK_EQ(needed_size, 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); // sep was not written + CHECK_EQ(buf.first(1), csubstr("1")); + CHECK_EQ(accum(), csubstr("1")); + } + SUBCASE("1 2") + { + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(0), sep, b1, b2); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(2), csubstr("++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(1), sep, b1, b2); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("1")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(seplen), sep, b1, b2); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("22")); + CHECK_EQ(accum(), csubstr("19000922")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf, sep, b1, b2); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("22")); + CHECK_EQ(accum(), csubstr("19000922")); + } + SUBCASE("2 1") + { + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(0), sep, b2, b1); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(2), csubstr("++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(1), sep, b2, b1); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(2), csubstr("++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(seplen), sep, b2, b1); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("10")); + CHECK_EQ(accum(), csubstr("22900091")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf, sep, b2, b1); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("10")); + CHECK_EQ(accum(), csubstr("22900091")); + } + SUBCASE("1 2 3 4") + { + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(0), sep, b1, b2, b3, b4); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(1), sep, b1, b2, b3, b4); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); + CHECK_EQ(accum(), csubstr("1")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(2), sep, b1, b2, b3, b4); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); + CHECK_EQ(accum(), csubstr("1")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(3), sep, b1, b2, b3, b4); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); + CHECK_EQ(accum(), csubstr("1")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf, sep, b1, b2, b3, b4); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 7); + CHECK_EQ(buf.first(4), csubstr("4444")); + CHECK_EQ(accum(), csubstr("1900092290009333900094444")); + } + SUBCASE("4 3 2 1") + { + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(0), sep, b4, b3, b2, b1); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(1), sep, b4, b3, b2, b1); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(2), sep, b4, b3, b2, b1); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf.first(3), sep, b4, b3, b2, b1); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + needed_size = T::call_catsep_dump(buf, sep, b4, b3, b2, b1); + CHECK_EQ(needed_size, seplen); + CHECK_EQ(DumpChecker::s_num_calls, 7); + CHECK_EQ(buf.first(4), csubstr("1000")); + CHECK_EQ(accum(), csubstr("4444900093339000922900091")); + } +} + + +TEST_CASE_TEMPLATE("catsep_dump_resume", T, CatDumpTplArg, CatDumpFnArg) +{ + using namespace buffers; + substr buf = DumpChecker::s_workspace; + auto accum = [&]{ return csubstr(DumpChecker::s_accum).first(DumpChecker::s_accum_pos); }; + SUBCASE("1") + { + DumpChecker::s_reset(); + DumpResults ret = T::call_catsep_dump_resume(buf.first(0), sep, b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_EQ(ret.bufsize, 1); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(1), csubstr("+")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_catsep_dump_resume(ret, buf.first(1), sep, b1); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_EQ(ret.bufsize, 1); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(1), csubstr("1")); + CHECK_EQ(accum(), csubstr("1")); + ret = T::call_catsep_dump_resume(ret, buf, sep, b1); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_EQ(ret.bufsize, 1); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(1), csubstr("1")); + CHECK_EQ(accum(), csubstr("1")); + } + SUBCASE("1 2") + { + DumpChecker::s_reset(); + DumpResults ret = T::call_catsep_dump_resume(buf.first(0), sep, b1, b2); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_EQ(ret.bufsize, seplen); // finds the buf size at once + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(2), csubstr("++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_catsep_dump_resume(ret, buf.first(1), sep, b1, b2); + CHECK_UNARY(ret.success_until(0)); // b1 + CHECK_UNARY(!ret.success_until(1)); // sep + CHECK_UNARY(!ret.success_until(2)); // b2 + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); // sep + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("1")); + ret = T::call_catsep_dump_resume(ret, buf.first(2), sep, b1, b2); + CHECK_UNARY(ret.success_until(0)); // b1 + CHECK_UNARY(!ret.success_until(1)); // sep + CHECK_UNARY(!ret.success_until(2)); // b2 + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); // sep + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("1")); + ret = T::call_catsep_dump_resume(ret, buf.first(seplen), sep, b1, b2); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, 2); + CHECK_EQ(ret.argfail(), 3); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("22")); + CHECK_EQ(accum(), csubstr("19000922")); + } + SUBCASE("1 2 3 4") + { + DumpChecker::s_reset(); + DumpResults ret = T::call_catsep_dump_resume(buf.first(0), sep, b1, b2, b3, b4); + CHECK_UNARY(!ret.success_until(0)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(4), csubstr("++++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_catsep_dump_resume(ret, buf.first(1), sep, b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); // failed while writing sep + CHECK_EQ(accum(), csubstr("1")); + ret = T::call_catsep_dump_resume(ret, buf.first(2), sep, b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); // failed while writing sep + CHECK_EQ(accum(), csubstr("1")); + ret = T::call_catsep_dump_resume(ret, buf.first(3), sep, b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); + CHECK_EQ(accum(), csubstr("1")); + ret = T::call_catsep_dump_resume(ret, buf.first(4), sep, b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); + CHECK_EQ(accum(), csubstr("1")); + ret = T::call_catsep_dump_resume(ret, buf.first(seplen), sep, b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_UNARY(ret.success_until(3)); + CHECK_UNARY(ret.success_until(4)); + CHECK_UNARY(ret.success_until(5)); + CHECK_UNARY(ret.success_until(6)); + CHECK_UNARY(!ret.success_until(7)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, 6); + CHECK_EQ(ret.argfail(), 7); + CHECK_EQ(DumpChecker::s_num_calls, 7); + CHECK_EQ(buf.first(seplen), csubstr("44449")); + CHECK_EQ(accum(), csubstr("1900092290009333900094444")); + } + SUBCASE("4 3 2 1") + { + DumpChecker::s_reset(); + DumpResults ret = T::call_catsep_dump_resume(buf.first(0), sep, b4, b3, b2, b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(4), csubstr("++++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_catsep_dump_resume(ret, buf.first(1), sep, b4, b3, b2, b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + ret = T::call_catsep_dump_resume(ret, buf.first(2), sep, b4, b3, b2, b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(4), csubstr("++++")); + CHECK_EQ(accum(), csubstr("")); + ret = T::call_catsep_dump_resume(ret, buf.first(seplen), sep, b4, b3, b2, b1); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_UNARY(ret.success_until(3)); + CHECK_UNARY(ret.success_until(4)); + CHECK_UNARY(ret.success_until(5)); + CHECK_UNARY(ret.success_until(6)); + CHECK_UNARY(!ret.success_until(7)); + CHECK_EQ(ret.bufsize, seplen); + CHECK_EQ(ret.lastok, 6); + CHECK_EQ(ret.argfail(), 7); + CHECK_EQ(DumpChecker::s_num_calls, 7); + CHECK_EQ(buf.first(seplen), csubstr("10009")); + CHECK_EQ(accum(), csubstr("4444900093339000922900091")); + } + SUBCASE("1 2 3 4 with seplen==3") + { + int s = 999; + DumpChecker::s_reset(); + DumpResults ret = T::call_catsep_dump_resume(buf.first(0), s, b1, b2, b3, b4); + CHECK_UNARY(!ret.success_until(0)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(4), csubstr("++++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_catsep_dump_resume(ret, buf.first(1), s, b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); // failed while writing sep + CHECK_EQ(accum(), csubstr("1")); + ret = T::call_catsep_dump_resume(ret, buf.first(2), s, b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); // b1 + CHECK_UNARY(!ret.success_until(1)); // s + CHECK_UNARY(!ret.success_until(2)); // b2 + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(4), csubstr("1+++")); // failed while writing sep + CHECK_EQ(accum(), csubstr("1")); + ret = T::call_catsep_dump_resume(ret, buf.first(3), s, b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_UNARY(ret.success_until(3)); + CHECK_UNARY(ret.success_until(4)); + CHECK_UNARY(ret.success_until(5)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, 5); + CHECK_EQ(ret.argfail(), 6); + CHECK_EQ(DumpChecker::s_num_calls, 6); + CHECK_EQ(buf.first(4), csubstr("999+")); // failed while writing b4 + CHECK_EQ(accum(), csubstr("199922999333999")); + ret = T::call_catsep_dump_resume(ret, buf.first(4), s, b1, b2, b3, b4); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_UNARY(ret.success_until(3)); + CHECK_UNARY(ret.success_until(4)); + CHECK_UNARY(ret.success_until(5)); + CHECK_UNARY(ret.success_until(6)); + CHECK_UNARY(!ret.success_until(7)); + CHECK_EQ(ret.bufsize, 4); + CHECK_EQ(ret.lastok, 6); + CHECK_EQ(ret.argfail(), 7); + CHECK_EQ(DumpChecker::s_num_calls, 7); + CHECK_EQ(buf.first(5), csubstr("4444+")); + CHECK_EQ(accum(), csubstr("1999229993339994444")); + } +} + + +TEST_CASE_TEMPLATE("format_dump", T, CatDumpTplArg, CatDumpFnArg) +{ + using namespace buffers; + size_t needed_size; + substr buf = DumpChecker::s_workspace; + auto accum = [&]{ return csubstr(DumpChecker::s_accum).first(DumpChecker::s_accum_pos); }; + SUBCASE("no buffer is needed for strings") + { + csubstr fmt = "{}-{}-{}-{}"; + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(0), fmt, "1", "22", "333", "4444"); // no buffer! + CHECK_EQ(needed_size, 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(1), csubstr("+")); + CHECK_EQ(accum(), csubstr("")); + DumpChecker::s_reset(); + CHECK_EQ(DumpChecker::s_num_calls, 0); + needed_size = T::call_format_dump(buf.first(1), fmt, "1", "22", "333", "4444"); // 1-len buffer, unused + CHECK_EQ(needed_size, 0); // no intermediate serialization is needed, since these are strings + CHECK_EQ(DumpChecker::s_num_calls, 7); // calls everything even when the buffer is empty + CHECK_EQ(accum(), csubstr("1-22-333-4444")); // dumped the full format string + } + SUBCASE("0") + { + csubstr fmt = "01234567890123456789"; + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(0), fmt, b1, b2, b3, b4); + CHECK_EQ(needed_size, 0); // the longest sized argument format argument + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls when the buffer is empty + CHECK_EQ(buf.first(needed_size), csubstr("")); // nothing was written + CHECK_EQ(accum(), csubstr("")); // dumped the full format string + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf, fmt, b1, b2, b3, b4); + CHECK_EQ(needed_size, 0); // the longest sized argument format argument + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(needed_size), csubstr("")); // nothing was written + CHECK_EQ(accum(), fmt); // dumped the full format string + } + SUBCASE("1") + { + // ____1____ 2 __3__ + csubstr fmt = "012345678_{}_34567"; + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(0), fmt, b1); + CHECK_EQ(needed_size, 1); // the longest sized argument format argument + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(2), csubstr("++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); // dumped first part of the format string + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(1), fmt, b1); + CHECK_EQ(needed_size, 1); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("012345678_1_34567")); + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(1), fmt, b1, b2, b3, b4); // check that extra arguments are ignored + CHECK_EQ(needed_size, 1); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("012345678_1_34567")); + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(1), fmt); // check that missing arguments are skipped + CHECK_EQ(needed_size, 0u); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(2), csubstr("++")); + CHECK_EQ(accum(), csubstr("012345678_{}_34567")); + } + SUBCASE("1 2") + { + // ____1____ 2 __3__ 4 _5_ + csubstr fmt = "012345678_{}_34567_{}_aaa"; + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(0), fmt, b1, b2); + CHECK_EQ(needed_size, 2); // the longest sized argument format argument + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(2), csubstr("++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); // dumped first part of the format string + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(1), fmt, b1, b2); + CHECK_EQ(needed_size, 2); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("012345678_1_34567_")); + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(2), fmt, b1, b2); + CHECK_EQ(needed_size, 2); + CHECK_EQ(DumpChecker::s_num_calls, 5); + CHECK_EQ(buf.first(2), csubstr("22")); + CHECK_EQ(accum(), csubstr("012345678_1_34567_22_aaa")); + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(1), fmt); // check that missing arguments are skipped + CHECK_EQ(needed_size, 0u); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(2), csubstr("++")); + CHECK_EQ(accum(), csubstr("012345678_{}_34567_{}_aaa")); + } + SUBCASE("1 2 3") + { + // 1 2 3 4 5 6 + csubstr fmt = "012345678_{}_34567_{}_aaa___{}"; + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(0), fmt, b1, b2, b3); + CHECK_EQ(needed_size, 3); // the longest sized argument format argument + CHECK_EQ(DumpChecker::s_num_calls, 0); + CHECK_EQ(buf.first(2), csubstr("++")); // nothing was written + CHECK_EQ(accum(), csubstr("")); // dumped first part of the format string + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(1), fmt, b1, b2, b3); + CHECK_EQ(needed_size, 3); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("012345678_1_34567_")); + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(2), fmt, b1, b2, b3); + CHECK_EQ(needed_size, 3); + CHECK_EQ(DumpChecker::s_num_calls, 5); + CHECK_EQ(buf.first(2), csubstr("22")); + CHECK_EQ(accum(), csubstr("012345678_1_34567_22_aaa___")); + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf.first(3), fmt, b1, b2, b3); + CHECK_EQ(needed_size, 3); + CHECK_EQ(DumpChecker::s_num_calls, 6); + CHECK_EQ(buf.first(2), csubstr("33")); + CHECK_EQ(accum(), csubstr("012345678_1_34567_22_aaa___333")); + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf, fmt); // check that missing arguments are skipped + CHECK_EQ(needed_size, 0u); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(2), csubstr("++")); + CHECK_EQ(accum(), csubstr("012345678_{}_34567_{}_aaa___{}")); + DumpChecker::s_reset(); + needed_size = T::call_format_dump(buf, fmt, b1); // check that missing arguments are skipped + CHECK_EQ(needed_size, 1); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("012345678_1_34567_{}_aaa___{}")); + } +} + + +TEST_CASE_TEMPLATE("format_dump_resume", T, CatDumpTplArg, CatDumpFnArg) +{ + using namespace buffers; + substr buf = DumpChecker::s_workspace; + auto accum = [&]{ return csubstr(DumpChecker::s_accum).first(DumpChecker::s_accum_pos); }; + SUBCASE("1") + { + csubstr fmt = "aaa_then_{}_then_bbb"; + DumpChecker::s_reset(); + DumpResults ret = T::call_format_dump_resume(buf.first(0), fmt, b1); + CHECK_UNARY(!ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_UNARY(!ret.success_until(2)); + CHECK_EQ(ret.bufsize, 1); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(1), csubstr("+")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_format_dump_resume(ret, buf.first(1), fmt, b1); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_EQ(ret.bufsize, 1); + CHECK_EQ(ret.lastok, 2); + CHECK_EQ(ret.argfail(), 3); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("aaa_then_1_then_bbb")); + } + SUBCASE("2") + { + csubstr fmt = "aaa_then_{}_then_bbb_then_{}__then_epilogue"; + DumpChecker::s_reset(); + DumpResults ret = T::call_format_dump_resume(buf.first(0), fmt, b1, b2); + CHECK_UNARY(!ret.success_until(0)); + CHECK_EQ(ret.bufsize, 2); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(1), csubstr("+")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_format_dump_resume(ret, buf.first(1), fmt, b1, b2); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_UNARY(!ret.success_until(3)); + CHECK_EQ(ret.bufsize, 2); + CHECK_EQ(ret.lastok, 2); + CHECK_EQ(ret.argfail(), 3); + CHECK_EQ(DumpChecker::s_num_calls, 3); + CHECK_EQ(buf.first(2), csubstr("1+")); + CHECK_EQ(accum(), csubstr("aaa_then_1_then_bbb_then_")); + ret = T::call_format_dump_resume(ret, buf.first(2), fmt, b1, b2); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(ret.success_until(1)); + CHECK_UNARY(ret.success_until(2)); + CHECK_UNARY(ret.success_until(3)); + CHECK_UNARY(ret.success_until(4)); + CHECK_EQ(ret.bufsize, 2); + CHECK_EQ(ret.lastok, 4); + CHECK_EQ(ret.argfail(), 5); + CHECK_EQ(DumpChecker::s_num_calls, 5); + CHECK_EQ(buf.first(2), csubstr("22")); + CHECK_EQ(accum(), csubstr("aaa_then_1_then_bbb_then_22__then_epilogue")); + } + SUBCASE("no args") + { + csubstr fmt = "no args { -- }"; + DumpChecker::s_reset(); + DumpResults ret = T::call_format_dump_resume(buf.first(0), fmt, b1, b2); + CHECK_UNARY(!ret.success_until(0)); + CHECK_EQ(ret.bufsize, 0); + CHECK_EQ(ret.lastok, DumpResults::noarg); + CHECK_EQ(ret.argfail(), 0); + CHECK_EQ(DumpChecker::s_num_calls, 0); // no calls to dump + CHECK_EQ(buf.first(1), csubstr("+")); // nothing was written + CHECK_EQ(accum(), csubstr("")); + ret = T::call_format_dump_resume(ret, buf.first(1), fmt, b1, b2); + CHECK_UNARY(ret.success_until(0)); + CHECK_UNARY(!ret.success_until(1)); + CHECK_EQ(ret.bufsize, 0); + CHECK_EQ(ret.lastok, 0); + CHECK_EQ(ret.argfail(), 1); + CHECK_EQ(DumpChecker::s_num_calls, 1); + CHECK_EQ(buf.first(2), "++"); + CHECK_EQ(accum(), fmt); + } +} + +} // namespace c4 + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_enum.cpp b/thirdparty/ryml/ext/c4core/test/test_enum.cpp new file mode 100644 index 000000000..b2dad0bbd --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_enum.cpp @@ -0,0 +1,158 @@ +#ifndef C4CORE_SINGLE_HEADER +#include <c4/std/std.hpp> +#include <c4/substr.hpp> +#include <c4/enum.hpp> +#endif + +#include <c4/test.hpp> + +#include "./test_enum_common.hpp" +#include "c4/libtest/supprwarn_push.hpp" +#include <vector> + + +TEST_CASE("eoffs.simple_enum") +{ + using namespace c4; + CHECK_EQ(eoffs_cls<MyEnum>(), 0); + CHECK_EQ(eoffs_pfx<MyEnum>(), 0); +} + +TEST_CASE("eoffs.scoped_enum") +{ + using namespace c4; + CHECK_EQ(eoffs_cls<MyEnumClass>(), strlen("MyEnumClass::")); + CHECK_EQ(eoffs_pfx<MyEnumClass>(), 0); +} + +TEST_CASE("eoffs.simple_bitmask") +{ + using namespace c4; + CHECK_EQ(eoffs_cls<MyBitmask>(), 0); + CHECK_EQ(eoffs_pfx<MyBitmask>(), strlen("BM_")); +} + +TEST_CASE("eoffs.scoped_bitmask") +{ + using namespace c4; + CHECK_EQ(eoffs_cls<MyBitmaskClass>(), strlen("MyBitmaskClass::")); + CHECK_EQ(eoffs_pfx<MyBitmaskClass>(), strlen("MyBitmaskClass::BM_")); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +#ifdef __clang__ +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# if __GNUC__ >= 6 +# pragma GCC diagnostic ignored "-Wnull-dereference" +# endif +#endif + +template<typename Enum> +void cmp_enum(Enum lhs, Enum rhs) +{ + using I = typename std::underlying_type<Enum>::type; + CHECK_EQ(static_cast<I>(lhs), static_cast<I>(rhs)); +} + +template<class Enum> +void test_esyms() +{ + auto ss = c4::esyms<Enum>(); + CHECK_NE(ss.size(), 0); + CHECK_FALSE(ss.empty()); + for(auto s : ss) + { + REQUIRE_NE(ss.find(s.name), nullptr); + REQUIRE_NE(ss.find(s.value), nullptr); + CHECK_STREQ(ss.find(s.name)->name, s.name); + CHECK_STREQ(ss.find(s.value)->name, s.name); + cmp_enum(ss.find(s.name)->value, s.value); + cmp_enum(ss.find(s.value)->value, s.value); + } +} + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + + +TEST_CASE("esyms.simple_enum") +{ + test_esyms<MyEnum>(); +} + +TEST_CASE("esyms.scoped_enum") +{ + test_esyms<MyEnumClass>(); +} + +TEST_CASE("esyms.simple_bitmask") +{ + test_esyms<MyBitmask>(); +} + +TEST_CASE("esyms.scoped_bitmask") +{ + test_esyms<MyBitmaskClass>(); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +template<typename Enum> +void test_e2str() +{ + using namespace c4; + using I = typename std::underlying_type<Enum>::type; + auto ss = esyms<Enum>(); + CHECK_NE(ss.size(), 0); + CHECK_FALSE(ss.empty()); + for(auto const& p : ss) + { + // test round trips + cmp_enum(str2e<Enum>(e2str(p.value)), p.value); + CHECK_STREQ(e2str(str2e<Enum>(p.name)), p.name); + } +} + + +TEST_CASE("e2str.simple_enum") +{ + test_e2str<MyEnum>(); +} + +TEST_CASE("e2str.scoped_enum") +{ + test_e2str<MyEnumClass>(); + cmp_enum(c4::str2e<MyEnumClass>("MyEnumClass::FOO"), MyEnumClass::FOO); + cmp_enum(c4::str2e<MyEnumClass>("FOO"), MyEnumClass::FOO); +} + +TEST_CASE("e2str.simple_bitmask") +{ + test_e2str<MyBitmask>(); + cmp_enum(c4::str2e<MyBitmask>("BM_FOO"), BM_FOO); + cmp_enum(c4::str2e<MyBitmask>("FOO"), BM_FOO); +} + +TEST_CASE("e2str.scoped_bitmask") +{ + using I = typename std::underlying_type<MyBitmaskClass>::type; + test_e2str<MyBitmaskClass>(); + cmp_enum(c4::str2e<MyBitmaskClass>("MyBitmaskClass::BM_FOO"), MyBitmaskClass::BM_FOO); + cmp_enum(c4::str2e<MyBitmaskClass>("BM_FOO"), MyBitmaskClass::BM_FOO); + cmp_enum(c4::str2e<MyBitmaskClass>("FOO"), MyBitmaskClass::BM_FOO); +} + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_enum_common.hpp b/thirdparty/ryml/ext/c4core/test/test_enum_common.hpp new file mode 100644 index 000000000..9acd621cf --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_enum_common.hpp @@ -0,0 +1,213 @@ +#ifndef _C4_ENUM_COMMON_HPP_ +#define _C4_ENUM_COMMON_HPP_ + +#ifndef C4CORE_SINGLE_HEADER +#include <c4/enum.hpp> +#endif + +typedef enum { + FOO = 0, + BAR, + BAZ, +} MyEnum; + +namespace c4 { +template<> +inline const EnumSymbols<MyEnum> esyms<MyEnum>() +{ + static const EnumSymbols<MyEnum>::Sym rs[] = + { + {FOO, "FOO"}, + {BAR, "BAR"}, + {BAZ, "BAZ"}, + }; + EnumSymbols<MyEnum> r(rs); + return r; +} +} // namespace c4 + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +enum class MyEnumClass { + FOO = 0, + BAR, + BAZ, +}; + + +namespace c4 { +template<> +inline const EnumSymbols<MyEnumClass> esyms<MyEnumClass>() +{ + static const EnumSymbols<MyEnumClass>::Sym rs[] = + { + {MyEnumClass::FOO, "MyEnumClass::FOO"}, + {MyEnumClass::BAR, "MyEnumClass::BAR"}, + {MyEnumClass::BAZ, "MyEnumClass::BAZ"}, + }; + EnumSymbols<MyEnumClass> r(rs); + return r; +} + + +template<> +inline size_t eoffs_cls<MyEnumClass>() +{ + return 13; // same as strlen("MyEnumClass::") +} +} // namespace c4 + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +typedef enum { + BM_NONE = 0, + BM_FOO = 1 << 0, + BM_BAR = 1 << 1, + BM_BAZ = 1 << 2, + BM_FOO_BAR = BM_FOO|BM_BAR, + BM_FOO_BAR_BAZ = BM_FOO|BM_BAR|BM_BAZ, +} MyBitmask; + +namespace c4 { +template<> +inline const EnumSymbols<MyBitmask> esyms<MyBitmask>() +{ + static const EnumSymbols<MyBitmask>::Sym rs[] = + { + {BM_NONE, "BM_NONE"}, + {BM_FOO, "BM_FOO"}, + {BM_BAR, "BM_BAR"}, + {BM_BAZ, "BM_BAZ"}, + {BM_FOO_BAR, "BM_FOO_BAR"}, + {BM_FOO_BAR_BAZ, "BM_FOO_BAR_BAZ"}, + }; + EnumSymbols<MyBitmask> r(rs); + return r; +} + +template<> +inline size_t eoffs_pfx<MyBitmask>() +{ + return 3; // same as strlen("BM_") +} +} // namespace c4 + + + +typedef enum { + // no null value + BM_KABOOM = 1, + BM_PAFF = 2, + BM_PEW = 4, + BM_POW = 7, +} BmWithoutNull; + + +namespace c4 { +template<> +inline const c4::EnumSymbols<BmWithoutNull> esyms<BmWithoutNull>() +{ + static const EnumSymbols<BmWithoutNull>::Sym rs[] = + { + {BM_KABOOM, "KABOOM"}, + {BM_PAFF , "PAFF"}, + {BM_PEW , "PEW"}, + {BM_POW , "POW"}, + }; + EnumSymbols<BmWithoutNull> r(rs); + return r; +} + +template<> +inline size_t eoffs_pfx<BmWithoutNull>() +{ + return 3; // same as strlen("BM_") +} +} // namespace c4 + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +enum class MyBitmaskClass { + BM_NONE = 0, + BM_FOO = 1 << 0, + BM_BAR = 1 << 1, + BM_BAZ = 1 << 2, + BM_FOO_BAR = BM_FOO|BM_BAR, + BM_FOO_BAR_BAZ = BM_FOO|BM_BAR|BM_BAZ, +}; + +namespace c4 { + +template<> +inline const EnumSymbols<MyBitmaskClass> esyms<MyBitmaskClass>() +{ + static const EnumSymbols<MyBitmaskClass>::Sym rs[] = + { + {MyBitmaskClass::BM_NONE, "MyBitmaskClass::BM_NONE"}, + {MyBitmaskClass::BM_FOO, "MyBitmaskClass::BM_FOO"}, + {MyBitmaskClass::BM_BAR, "MyBitmaskClass::BM_BAR"}, + {MyBitmaskClass::BM_BAZ, "MyBitmaskClass::BM_BAZ"}, + {MyBitmaskClass::BM_FOO_BAR, "MyBitmaskClass::BM_FOO_BAR"}, + {MyBitmaskClass::BM_FOO_BAR_BAZ, "MyBitmaskClass::BM_FOO_BAR_BAZ"}, + }; + EnumSymbols<MyBitmaskClass> r(rs); + return r; +} + +template<> inline size_t eoffs_cls< MyBitmaskClass >() +{ + return 16; // same as strlen("MyBitmaskClass::") +} +template<> inline size_t eoffs_pfx< MyBitmaskClass >() +{ + return 19; // same as strlen("MyBitmaskClass::BM_") +} + +} // namespace c4 + + +enum class BmClassWithoutNull { + // no null value + BM_KABOOM = 1, + BM_PAFF = 2, + BM_PEW = 4, + BM_POW = 7, +}; + + +namespace c4 { +template<> +inline const c4::EnumSymbols<BmClassWithoutNull> esyms<BmClassWithoutNull>() +{ + static const EnumSymbols<BmClassWithoutNull>::Sym rs[] = + { + {BmClassWithoutNull::BM_KABOOM, "BmClassWithoutNull::BM_KABOOM"}, + {BmClassWithoutNull::BM_PAFF , "BmClassWithoutNull::BM_PAFF"}, + {BmClassWithoutNull::BM_PEW , "BmClassWithoutNull::BM_PEW"}, + {BmClassWithoutNull::BM_POW , "BmClassWithoutNull::BM_POW"}, + }; + EnumSymbols<BmClassWithoutNull> r(rs); + return r; +} + +template<> inline size_t eoffs_cls<BmClassWithoutNull>() +{ + return strlen("BmClassWithoutNull::"); +} +template<> inline size_t eoffs_pfx<BmClassWithoutNull>() +{ + return strlen("BmClassWithoutNull::BM_"); +} +} // namespace c4 + + +#endif /* _C4_ENUM_COMMON_HPP_ */ diff --git a/thirdparty/ryml/ext/c4core/test/test_error.cpp b/thirdparty/ryml/ext/c4core/test/test_error.cpp new file mode 100644 index 000000000..96584ab92 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_error.cpp @@ -0,0 +1,635 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/error.hpp" +#endif + +#include "c4/test.hpp" +#include "c4/libtest/supprwarn_push.hpp" + +C4_BEGIN_HIDDEN_NAMESPACE +bool got_an_error = false; +void error_callback(const char *msg, size_t msg_sz) +{ + CHECK_EQ(strncmp(msg, "bla bla", msg_sz), 0); + CHECK_EQ(msg_sz, 7); + got_an_error = true; +} +inline c4::ScopedErrorSettings tmp_err() +{ + got_an_error = false; + return c4::ScopedErrorSettings(c4::ON_ERROR_CALLBACK, error_callback); +} +C4_END_HIDDEN_NAMESPACE + +namespace c4 { + +TEST_CASE("Error.scoped_callback") +{ + auto orig = get_error_callback(); + { + auto tmp = tmp_err(); + CHECK_EQ(get_error_callback() == error_callback, true); + C4_ERROR("bla bla"); + CHECK_EQ(got_an_error, true); + } + CHECK_EQ(get_error_callback() == orig, true); +} + +} // namespace c4 + +TEST_CASE("Error.outside_of_c4_namespace") +{ + auto orig = c4::get_error_callback(); + { + auto tmp = tmp_err(); + CHECK_EQ(c4::get_error_callback() == error_callback, true); + C4_ERROR("bla bla"); + CHECK_EQ(got_an_error, true); + } + CHECK_EQ(c4::get_error_callback() == orig, true); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// WIP: new error handling code + + +#include <string> // temporary; just for the exception example +#include <iostream> // temporary; just for the exception example + +namespace c4 { + +#define C4_ERR_FMT_BUFFER_SIZE 256 + +using locref = c4::srcloc const& C4_RESTRICT; + +using pfn_err = void (*)(locref loc, void *data); +using pfn_warn = void (*)(locref loc, void *data); +using pfn_msg_begin = void (*)(locref loc, void *data); +using pfn_msg_part = void (*)(const char* msg, size_t size, void *data); +using pfn_msg_end = void (*)(void *data); + +struct ErrorCallbacks +{ + void *user_data; + + pfn_err err; + pfn_warn warn; + pfn_msg_begin msg_begin; + pfn_msg_part msg_part; + pfn_msg_end msg_end; + + bool msg_enabled() const { return msg_begin != nullptr; } + + template<size_t N> + void msg(const char (&s)[N]) + { + msg_part(s, N-1, user_data); + } + void msg(const char *msg, size_t sz) + { + msg_part(msg, sz, user_data); + } + void msg(char c) + { + msg_part(&c, 1, user_data); + } +}; + +#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 && __GNUC__ < 5 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif +TEST_CASE("ErrorCallbacks.default_obj") +{ + ErrorCallbacks cb {}; + CHECK_EQ(cb.user_data, nullptr); + CHECK_EQ(cb.err, nullptr); + CHECK_EQ(cb.warn, nullptr); + CHECK_EQ(cb.msg_begin, nullptr); + CHECK_EQ(cb.msg_part, nullptr); + CHECK_EQ(cb.msg_end, nullptr); +} +#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 && __GNUC__ < 5 +#pragma GCC diagnostic pop +#endif + +template<class ErrBhv> +struct ErrorCallbacksBridgeFull +{ + ErrorCallbacks callbacks() const + { + return { + (ErrBhv*)this, + ErrorCallbacksBridgeFull<ErrBhv>::on_err, + ErrorCallbacksBridgeFull<ErrBhv>::on_warn, + ErrorCallbacksBridgeFull<ErrBhv>::on_msg_begin, + ErrorCallbacksBridgeFull<ErrBhv>::on_msg_part, + ErrorCallbacksBridgeFull<ErrBhv>::on_msg_end, + }; + } + static void on_err(locref loc, void *data) + { + ((ErrBhv*)data)->err(loc); + } + static void on_warn(locref loc, void *data) + { + ((ErrBhv*)data)->warn(loc); + } + static void on_msg_begin(locref loc, void *data) + { + ((ErrBhv*)data)->msg_begin(loc); + } + static void on_msg_part(const char *part, size_t size, void *data) + { + ((ErrBhv*)data)->msg_part(part, size); + } + static void on_msg_end(void *data) + { + ((ErrBhv*)data)->msg_end(); + } +}; + +template<class ErrBhv> +struct ErrorCallbacksBridge +{ + ErrorCallbacks callbacks() const + { + return { + (ErrBhv*)this, + ErrorCallbacksBridge<ErrBhv>::on_err, + ErrorCallbacksBridge<ErrBhv>::on_warn, + (pfn_msg_begin)nullptr, + (pfn_msg_part)nullptr, + (pfn_msg_end)nullptr + }; + } + static void on_err(locref loc, void *data) + { + ((ErrBhv*)data)->err(loc); + } + static void on_warn(locref loc, void *data) + { + ((ErrBhv*)data)->warn(loc); + } +}; + + +void fputi(int val, FILE *f); + +void _errmsg(locref loc) +{ + fputs(loc.file, stderr); + fputc(':', stderr); + fputi(loc.line, stderr); + fputs(": ", stderr); + fflush(stderr); +} + +void _errmsg(const char *part, size_t part_size) +{ + fwrite(part, 1u, part_size, stderr); + fflush(stderr); +} + +/** example implementation using old-style abort */ +struct ErrorBehaviorAbort : public ErrorCallbacksBridgeFull<ErrorBehaviorAbort> +{ + static void msg_begin(locref loc) + { + fputc('\n', stderr); + _errmsg(loc); + } + static void msg_part(const char *part, size_t part_size) + { + _errmsg(part, part_size); + } + static void msg_end() + { + fputc('\n', stderr); + fflush(stderr); + } + static void err(locref) + { + abort(); + } + static void warn(locref) + { + // nothing to do + } +}; + + +TEST_CASE("ErrorBehaviorAbort.default_obj") +{ + ErrorBehaviorAbort bhv; + auto cb = bhv.callbacks(); + CHECK_NE(cb.user_data, nullptr); + CHECK_NE(cb.err, nullptr); + CHECK_NE(cb.warn, nullptr); + CHECK_NE(cb.msg_begin, nullptr); + CHECK_NE(cb.msg_part, nullptr); + CHECK_NE(cb.msg_end, nullptr); +} + + +void fputi(int val, FILE *f); +void _append(std::string *s, int line); + +/** example implementation using vanilla c++ std::runtime_error */ +struct ErrorBehaviorRuntimeError : public ErrorCallbacksBridgeFull<ErrorBehaviorRuntimeError> +{ + std::string exc_msg{}; + + void msg_begin(locref loc) + { + exc_msg.reserve(strlen(loc.file) + 16); + exc_msg = '\n'; + exc_msg += loc.file; + exc_msg += ':'; + _append(&exc_msg, loc.line); + exc_msg += ": "; + } + void msg_part(const char *part, size_t part_size) + { + exc_msg.append(part, part_size); + } + void msg_end() + { + std::cerr << exc_msg << "\n"; + } + void err(locref) + { + throw std::runtime_error(exc_msg); + } + void warn(locref) + { + } +}; + + + +TEST_CASE("ErrorBehaviorRuntimeError.default_obj") +{ + ErrorBehaviorRuntimeError bhv; + auto cb = bhv.callbacks(); + CHECK_NE(cb.user_data, nullptr); + CHECK_NE(cb.err, nullptr); + CHECK_NE(cb.warn, nullptr); + CHECK_NE(cb.msg_begin, nullptr); + CHECK_NE(cb.msg_part, nullptr); + CHECK_NE(cb.msg_end, nullptr); +} + + + + +ErrorBehaviorAbort s_err_abort = ErrorBehaviorAbort(); +ErrorCallbacks s_err_callbacks = s_err_abort.callbacks(); + + +void new_handle_error(locref loc, size_t msg_size, const char *msg) +{ + if(s_err_callbacks.msg_enabled()) + { + s_err_callbacks.msg_begin(loc, s_err_callbacks.user_data); + s_err_callbacks.msg("ERROR: "); + s_err_callbacks.msg(msg, msg_size); + s_err_callbacks.msg_end(s_err_callbacks.user_data); + } + s_err_callbacks.err(loc, s_err_callbacks.user_data); +} + +void new_handle_warning(locref loc, size_t msg_size, const char *msg) +{ + if(s_err_callbacks.msg_enabled()) + { + s_err_callbacks.msg_begin(loc, s_err_callbacks.user_data); + s_err_callbacks.msg("WARNING: "); + s_err_callbacks.msg(msg, msg_size); + s_err_callbacks.msg_end(s_err_callbacks.user_data); + } + s_err_callbacks.warn(loc, s_err_callbacks.user_data); +} + +template<size_t N> +C4_ALWAYS_INLINE void new_handle_error(locref loc, const char (&msg)[N]) +{ + new_handle_error(loc, N-1, msg); +} + +template<size_t N> +C4_ALWAYS_INLINE void new_handle_warning(locref loc, const char (&msg)[N]) +{ + new_handle_warning(loc, N-1, msg); +} + + +#define C4_ERROR_NEW(msg) c4::new_handle_error(C4_SRCLOC(), msg) +#define C4_WARNING_NEW(msg) c4::new_handle_warning(C4_SRCLOC(), msg) + +#define C4_ERROR_NEW_SZ(msg, msglen) c4::new_handle_error(C4_SRCLOC(), msglen, msg) +#define C4_WARNING_NEW_SZ(msg, msglen) c4::new_handle_warning(C4_SRCLOC(), msglen, msg) + +} // namespace c4 + +#ifndef C4CORE_SINGLE_HEADER +#include <c4/substr.hpp> +#include <c4/charconv.hpp> +#endif + +namespace c4 { + +void fputi(int val, FILE *f) +{ + char buf[16]; + size_t ret = c4::itoa(buf, val); + ret = ret < sizeof(buf) ? ret : sizeof(buf); + fwrite(buf, 1u, ret, f); +} + +// to avoid using std::to_string() +void _append(std::string *s, int line) +{ + auto sz = s->size(); + s->resize(sz + 16); + auto ret = itoa(substr(&((*s)[0]) + sz, 16u), line); + s->resize(sz + ret); + if(ret >= sz) + { + itoa(substr(&((*s)[0]) + sz, 16u), line); + } +} + +} // namespace c4 +template<class ErrorBehavior> +struct ScopedErrorBehavior +{ + c4::ErrorCallbacks m_prev; + ErrorBehavior m_tmp; + const char *m_name; + ScopedErrorBehavior(const char* name) : m_prev(c4::s_err_callbacks), m_tmp(), m_name(name) + { + c4::s_err_callbacks = m_tmp.callbacks(); + } + ~ScopedErrorBehavior() + { + c4::s_err_callbacks = m_prev; + } +}; +#define C4_TMP_ERR_BHV(bhv_ty) ScopedErrorBehavior<c4::bhv_ty>(#bhv_ty) + +template<size_t N> +void test_error_exception(const char (&msg)[N]) +{ + INFO(msg); + { + auto tmp1 = C4_TMP_ERR_BHV(ErrorBehaviorAbort); + + { + auto tmp2 = C4_TMP_ERR_BHV(ErrorBehaviorRuntimeError); + + bool got_exc = false; + try { + C4_ERROR_NEW(msg); + } + catch(std::exception const& e) { + // check that the error terminates with the given message + auto what = c4::to_csubstr(e.what()).last(N-1); + CHECK_EQ(what, msg); + got_exc = (what == msg); + } + CHECK(got_exc); + + got_exc = false; + try { + C4_ERROR_NEW_SZ(msg, N-1); + } + catch(std::exception const& e) { + // check that the error terminates with the given message + auto what = c4::to_csubstr(e.what()).last(N-1); + CHECK_EQ(what, msg); + got_exc = (what == msg); + } + CHECK(got_exc); + } + } +} + +template<size_t N> +void test_warning_exception(const char (&msg)[N]) +{ + auto tmp = C4_TMP_ERR_BHV(ErrorBehaviorRuntimeError); + C4_WARNING_NEW(msg); + auto const& wmsg = tmp.m_tmp.exc_msg; + REQUIRE_FALSE(wmsg.empty()); + REQUIRE_GT(wmsg.size(), N); + auto what = c4::to_csubstr(wmsg.c_str()).last(N-1); + CHECK_EQ(what, msg); + + C4_WARNING_NEW_SZ(msg, N-1); + REQUIRE_FALSE(wmsg.empty()); + REQUIRE_GT(wmsg.size(), N); + what = c4::to_csubstr(wmsg.c_str()).last(N-1); + CHECK_EQ(what, msg); +} + +TEST_CASE("error.exception") +{ + test_error_exception("some error with some message"); + test_error_exception("some error with another message"); +} + +TEST_CASE("warning.exception") +{ + test_warning_exception("some warning"); + test_warning_exception("some other warning"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +#ifndef C4CORE_SINGLE_HEADER +#include <c4/substr.hpp> +#include <c4/charconv.hpp> +#endif + +namespace c4 { + +template<class T> +void _err_send(T const& arg) +{ + char buf[C4_ERR_FMT_BUFFER_SIZE]; + size_t num = to_chars(buf, arg); + num = num < C4_ERR_FMT_BUFFER_SIZE ? num : C4_ERR_FMT_BUFFER_SIZE; + s_err_callbacks.msg_part(buf, num, s_err_callbacks.user_data); +} + + +size_t _find_fmt(const char *C4_RESTRICT fmt, size_t len) +{ + for(size_t i = 0; i < len; ++i) + { + if(fmt[i] != '{') + { + continue; + } + if(i + 1 == len) + { + break; + } + if(fmt[i+1] == '}') + { + return i; + } + } + return (size_t)-1; +} + +void _err_fmt(size_t fmt_size, const char *C4_RESTRICT fmt) +{ + s_err_callbacks.msg_part(fmt, fmt_size, s_err_callbacks.user_data); +} + +template<class Arg, class ...Args> +void _err_fmt(size_t fmt_size, const char *C4_RESTRICT fmt, Arg const& C4_RESTRICT arg, Args const& C4_RESTRICT ...args) +{ + size_t pos = _find_fmt(fmt, fmt_size); + if(pos == (size_t)-1) + { + s_err_callbacks.msg_part(fmt, fmt_size, s_err_callbacks.user_data); + return; + } + s_err_callbacks.msg_part(fmt, pos, s_err_callbacks.user_data); + _err_send(arg); + pos += 2; + _err_fmt(fmt_size - pos, fmt + pos, args...); +} + +template<class ...Args> +void err_fmt(locref loc, size_t fmt_size, const char *fmt, Args const& C4_RESTRICT ...args) +{ + s_err_callbacks.msg_begin(loc, s_err_callbacks.user_data); + s_err_callbacks.msg("ERROR: "); + _err_fmt(fmt_size, fmt, args...); + s_err_callbacks.msg_end(s_err_callbacks.user_data); + s_err_callbacks.err(loc, s_err_callbacks.user_data); +} + +template<class ...Args> +void warn_fmt(locref loc, size_t fmt_size, const char *fmt, Args const& C4_RESTRICT ...args) +{ + s_err_callbacks.msg_begin(loc, s_err_callbacks.user_data); + s_err_callbacks.msg("WARNING: "); + _err_fmt(fmt_size, fmt, args...); + s_err_callbacks.msg_end(s_err_callbacks.user_data); + s_err_callbacks.warn(loc, s_err_callbacks.user_data); +} + +template<size_t N, class ...Args> +void err_fmt(locref loc, const char (&fmt)[N], Args const& C4_RESTRICT ...args) +{ + err_fmt(loc, N-1, fmt, args...); +} + +template<size_t N, class ...Args> +void warn_fmt(locref loc, const char (&fmt)[N], Args const& C4_RESTRICT ...args) +{ + warn_fmt(loc, N-1, fmt, args...); +} + +#define C4_ERROR_FMT_NEW(fmt, ...) c4::err_fmt(C4_SRCLOC(), fmt, __VA_ARGS__) +#define C4_WARNING_FMT_NEW(msg, ...) c4::warn_fmt(C4_SRCLOC(), fmt, __VA_ARGS__) + +#define C4_ERROR_FMT_NEW_SZ(fmt, fmtlen, ...) c4::err_fmt(C4_SRCLOC(), fmtlen, fmt, __VA_ARGS__) +#define C4_WARNING_FMT_NEW_SZ(fmt, fmtlen, ...) c4::warn_fmt(C4_SRCLOC(), fmtlen, fmt, __VA_ARGS__) + +} // namespace c4 + +template<size_t N, size_t M, class... Args> +void test_error_fmt_exception(const char (&expected)[M], const char (&fmt)[N], Args const& ...args) +{ + INFO(expected); + { + auto tmp1 = C4_TMP_ERR_BHV(ErrorBehaviorAbort); + + { + auto tmp2 = C4_TMP_ERR_BHV(ErrorBehaviorRuntimeError); + + bool got_exc = false; + try { + C4_ERROR_FMT_NEW(fmt, args...); + } + catch(std::exception const& e) { + // check that the error terminates with the given message + auto what = c4::to_csubstr(e.what()).last(M-1); + CHECK_EQ(what, expected); + got_exc = (what == expected); + } + CHECK(got_exc); + + got_exc = false; + try { + C4_ERROR_FMT_NEW_SZ(fmt, N-1, args...); + } + catch(std::exception const& e) { + // check that the error terminates with the given message + auto what = c4::to_csubstr(e.what()).last(M-1); + CHECK_EQ(what, expected); + got_exc = (what == expected); + } + CHECK(got_exc); + } + } +} + +template<size_t N, size_t M, class... Args> +void test_warning_fmt_exception(const char (&expected)[M], const char (&fmt)[N], Args const& ...args) +{ + INFO(expected); + + auto tmp = C4_TMP_ERR_BHV(ErrorBehaviorRuntimeError); + auto const& wmsg = tmp.m_tmp.exc_msg; + C4_WARNING_FMT_NEW(fmt, args...); + REQUIRE_FALSE(wmsg.empty()); + REQUIRE_GT(wmsg.size(), M); + auto what = c4::to_csubstr(wmsg.c_str()).last(M-1); + CHECK_EQ(what, expected); + + C4_WARNING_FMT_NEW_SZ(fmt, N-1, args...); + REQUIRE_FALSE(wmsg.empty()); + REQUIRE_GT(wmsg.size(), M); + what = c4::to_csubstr(wmsg.c_str()).last(M-1); + CHECK_EQ(what, expected); +} + + +TEST_CASE("error.fmt") +{ + test_error_fmt_exception("aaa is 2 is it not?", + "{} is {} is it not?", "aaa", 2); + test_error_fmt_exception("aaa is bbb is it not?", + "{} is {} is it not?", "aaa", "bbb"); + test_error_fmt_exception("aaa is {} is it not?", + "{} is {} is it not?", "aaa"); + test_error_fmt_exception("aaa is {} is it not?", + "{} is {} is it not?", "aaa"); +} + +TEST_CASE("warning.fmt") +{ + test_warning_fmt_exception("aaa is 2 is it not?", + "{} is {} is it not?", "aaa", 2); + test_warning_fmt_exception("aaa is bbb is it not?", + "{} is {} is it not?", "aaa", "bbb"); + test_warning_fmt_exception("aaa is {} is it not?", + "{} is {} is it not?", "aaa"); + test_warning_fmt_exception("aaa is {} is it not?", + "{} is {} is it not?", "aaa"); +} + + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_error_exception.cpp b/thirdparty/ryml/ext/c4core/test/test_error_exception.cpp new file mode 100644 index 000000000..fae0020b8 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_error_exception.cpp @@ -0,0 +1,108 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/error.hpp" +#endif + +#include "c4/test.hpp" +#include <string> +#include <stdexcept> + + +C4_BEGIN_HIDDEN_NAMESPACE +bool got_an_error = false; +bool got_an_exception = false; +C4_END_HIDDEN_NAMESPACE + +void error_callback_throwing_exception(const char *msg_, size_t msg_sz) +{ + got_an_error = true; + c4::csubstr s(msg_, msg_sz); + if (s == "err1") throw 1; + else if(s == "err2") throw 2; + else if(s == "err3") throw 3; + else if(s == "err4") throw 4; + else throw std::runtime_error({msg_, msg_+msg_sz}); +} + +inline c4::ScopedErrorSettings tmp_err() +{ + got_an_error = false; + return c4::ScopedErrorSettings(c4::ON_ERROR_CALLBACK, error_callback_throwing_exception); +} + + +void test_exception(int which) +{ + if(which == 0) return; + CHECK_FALSE(got_an_exception); + CHECK_EQ(c4::get_error_callback() == error_callback_throwing_exception, false); + { + auto tmp = tmp_err(); + CHECK_EQ(got_an_error, false); + CHECK_EQ(c4::get_error_callback() == error_callback_throwing_exception, true); + try + { + if (which == 1) { C4_ERROR("err1"); } + else if(which == 2) { C4_ERROR("err2"); } + else if(which == 3) { C4_ERROR("err3"); } + else if(which == 4) { C4_ERROR("err4"); } + else { C4_ERROR("unknown error"); } + } + catch(int i) + { + got_an_exception = true; + CHECK_EQ(got_an_error, true); + CHECK_EQ(i, which); + throw; + } + catch(std::runtime_error const& e) + { + got_an_exception = true; + CHECK_EQ(got_an_error, true); + CHECK_STREQ(e.what(), "unknown error"); + throw; + } + // if we get here it means no exception was thrown + // so the test failed + FAIL("an exception was thrown"); + } + CHECK_EQ(c4::get_error_callback() == error_callback_throwing_exception, false); +} + + +// Although c4core does not use exceptions by default (*), you can have your +// error callback throw an exception which you can then catch on your code. +// This test covers that possibility. +// +// (*) Note that you can also configure c4core to throw an exception on error. + +TEST_CASE("error.exception_from_callback") +{ + // works! + got_an_exception = false; + CHECK_THROWS_AS(test_exception(-1), std::runtime_error); + CHECK(got_an_exception); + + got_an_exception = false; + CHECK_NOTHROW(test_exception(0)); + CHECK_FALSE(got_an_exception); + + got_an_exception = false; + CHECK_THROWS_AS(test_exception(1), int); + CHECK(got_an_exception); + + got_an_exception = false; + CHECK_THROWS_AS(test_exception(2), int); + CHECK(got_an_exception); + + got_an_exception = false; + CHECK_THROWS_AS(test_exception(3), int); + CHECK(got_an_exception); + + got_an_exception = false; + CHECK_THROWS_AS(test_exception(4), int); + CHECK(got_an_exception); + + got_an_exception = false; + CHECK_THROWS_AS(test_exception(6), std::runtime_error); + CHECK(got_an_exception); +} diff --git a/thirdparty/ryml/ext/c4core/test/test_format.cpp b/thirdparty/ryml/ext/c4core/test/test_format.cpp new file mode 100644 index 000000000..42a50dc75 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_format.cpp @@ -0,0 +1,1054 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/substr.hpp" +#include "c4/std/std.hpp" +#include "c4/format.hpp" +#endif + +#include <c4/test.hpp> +#include "c4/libtest/supprwarn_push.hpp" + +#ifdef __clang__ +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif + +namespace c4 { + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +TEST_CASE_TEMPLATE("to_chars.fmt.bin", T, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t) +{ + char bufc[128]; + substr buf(bufc); + + CHECK_EQ(to_chars_sub(buf, fmt::integral(T(21), T(2))), "0b10101"); + CHECK_EQ(to_chars_sub(buf, fmt::integral((T*)21, T(2))), "0b10101"); + CHECK_EQ(to_chars_sub(buf, fmt::integral((T const*)21, T(2))), "0b10101"); + CHECK_EQ(to_chars_sub(buf, fmt::integral(nullptr, T(2))), "0b0"); + CHECK_EQ(to_chars_sub(buf, fmt::bin(T(21))), "0b10101"); + CHECK_EQ(to_chars_sub(buf, fmt::bin((T*)21)), "0b10101"); + CHECK_EQ(to_chars_sub(buf, fmt::bin((T const*)21)), "0b10101"); + CHECK_EQ(to_chars_sub(buf, fmt::bin(nullptr)), "0b0"); +} + +TEST_CASE_TEMPLATE("to_chars.fmt.zpad.bin", T, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t) +{ + char bufc[128]; + substr buf(bufc); + using namespace fmt; + CHECK_EQ(to_chars_sub(buf, zpad(integral(T(21), T(2)), 8u)), "0b00010101"); + CHECK_EQ(to_chars_sub(buf, zpad(integral((T*)21, T(2)), 8u)), "0b00010101"); + CHECK_EQ(to_chars_sub(buf, zpad(integral((T const*)21, T(2)), 8u)), "0b00010101"); + CHECK_EQ(to_chars_sub(buf, zpad(bin(T(21)), 8u)), "0b00010101"); + CHECK_EQ(to_chars_sub(buf, zpad(bin((T*)21), 8u)), "0b00010101"); + CHECK_EQ(to_chars_sub(buf, zpad(bin((T const*)21), 8u)), "0b00010101"); +} + +TEST_CASE_TEMPLATE("to_chars.fmt.oct", T, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t) +{ + char bufc[128]; + substr buf(bufc); + + CHECK_EQ(to_chars_sub(buf, fmt::integral(T(65), T(8))), "0o101"); + CHECK_EQ(to_chars_sub(buf, fmt::integral((T*)65, T(8))), "0o101"); + CHECK_EQ(to_chars_sub(buf, fmt::integral((T const*)65, T(8))), "0o101"); + CHECK_EQ(to_chars_sub(buf, fmt::integral(nullptr, T(8))), "0o0"); + CHECK_EQ(to_chars_sub(buf, fmt::oct(T(65))), "0o101"); + CHECK_EQ(to_chars_sub(buf, fmt::oct((T*)65)), "0o101"); + CHECK_EQ(to_chars_sub(buf, fmt::oct((T const*)65)), "0o101"); + CHECK_EQ(to_chars_sub(buf, fmt::oct(nullptr)), "0o0"); +} + +TEST_CASE_TEMPLATE("to_chars.fmt.zpad.oct", T, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t) +{ + char bufc[128]; + substr buf(bufc); + using namespace fmt; + CHECK_EQ(to_chars_sub(buf, zpad(integral(T(65), T(8)), 5u)), "0o00101"); + CHECK_EQ(to_chars_sub(buf, zpad(integral((T*)65, T(8)), 5u)), "0o00101"); + CHECK_EQ(to_chars_sub(buf, zpad(integral((T const*)65, T(8)), 5u)), "0o00101"); + CHECK_EQ(to_chars_sub(buf, zpad(oct(T(65)), 5u)), "0o00101"); + CHECK_EQ(to_chars_sub(buf, zpad(oct((T*)65), 5u)), "0o00101"); + CHECK_EQ(to_chars_sub(buf, zpad(oct((T const*)65), 5u)), "0o00101"); +} + +TEST_CASE_TEMPLATE("to_chars.fmt.hex", T, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t) +{ + char bufc[128]; + substr buf(bufc); + CHECK_EQ(to_chars_sub(buf, fmt::integral(T(0x7f), T(16))), "0x7f"); + CHECK_EQ(to_chars_sub(buf, fmt::integral((T*)0x7f, T(16))), "0x7f"); + CHECK_EQ(to_chars_sub(buf, fmt::integral((T const*)0x7f, T(16))), "0x7f"); + CHECK_EQ(to_chars_sub(buf, fmt::integral(nullptr, T(16))), "0x0"); + CHECK_EQ(to_chars_sub(buf, fmt::hex(T(0x7f))), "0x7f"); + CHECK_EQ(to_chars_sub(buf, fmt::hex((T*)0x7f)), "0x7f"); + CHECK_EQ(to_chars_sub(buf, fmt::hex((T const*)0x7f)), "0x7f"); + CHECK_EQ(to_chars_sub(buf, fmt::hex(nullptr)), "0x0"); +} + +TEST_CASE_TEMPLATE("to_chars.fmt.zpad.hex", T, uint8_t, int8_t) +{ + char bufc[128]; + substr buf(bufc); + using namespace fmt; + buf.fill('?'); + CHECK_EQ(to_chars_sub(buf, zpad(integral(T(0x7f), T(16)), 5u)), "0x0007f"); + CHECK_EQ(to_chars_sub(buf, zpad(integral((T*)0x7f, T(16)), 5u)), "0x0007f"); + CHECK_EQ(to_chars_sub(buf, zpad(integral((T const*)0x7f, T(16)), 5u)), "0x0007f"); + CHECK_EQ(to_chars_sub(buf, zpad(hex(T(0x7f)), 5u)), "0x0007f"); + CHECK_EQ(to_chars_sub(buf, zpad(hex((T*)0x7f), 5u)), "0x0007f"); + CHECK_EQ(to_chars_sub(buf, zpad(hex((T const*)0x7f), 5u)), "0x0007f"); +} + + +TEST_CASE_TEMPLATE("to_chars.fmt.zpad", T, uint8_t, int8_t) +{ + char bufc[128]; + substr buf(bufc); + using namespace fmt; + CHECK_EQ(to_chars_sub(buf, zpad(T(10), 0)), "10"); + CHECK_EQ(to_chars_sub(buf, zpad(T(10), 1)), "10"); + CHECK_EQ(to_chars_sub(buf, zpad(T(10), 2)), "10"); + CHECK_EQ(to_chars_sub(buf, zpad(T(10), 3)), "010"); + CHECK_EQ(to_chars_sub(buf, zpad(T(10), 4)), "0010"); + CHECK_EQ(to_chars_sub(buf, zpad((T const*)17, 0)), "0x11"); + CHECK_EQ(to_chars_sub(buf, zpad((T const*)17, 1)), "0x11"); + CHECK_EQ(to_chars_sub(buf, zpad((T const*)17, 2)), "0x11"); + CHECK_EQ(to_chars_sub(buf, zpad((T const*)17, 3)), "0x011"); + CHECK_EQ(to_chars_sub(buf, zpad((T const*)17, 4)), "0x0011"); + CHECK_EQ(to_chars_sub(buf, zpad((T *)17, 0)), "0x11"); + CHECK_EQ(to_chars_sub(buf, zpad((T *)17, 1)), "0x11"); + CHECK_EQ(to_chars_sub(buf, zpad((T *)17, 2)), "0x11"); + CHECK_EQ(to_chars_sub(buf, zpad((T *)17, 3)), "0x011"); + CHECK_EQ(to_chars_sub(buf, zpad((T *)17, 4)), "0x0011"); + CHECK_EQ(to_chars_sub(buf, zpad(nullptr, 0)), "0x0"); + CHECK_EQ(to_chars_sub(buf, zpad(nullptr, 1)), "0x0"); + CHECK_EQ(to_chars_sub(buf, zpad(nullptr, 2)), "0x00"); + CHECK_EQ(to_chars_sub(buf, zpad(nullptr, 3)), "0x000"); + CHECK_EQ(to_chars_sub(buf, zpad(nullptr, 4)), "0x0000"); + CHECK_EQ(to_chars_sub(buf, zpad(integral(nullptr, T(10)), 0u)), "0"); + CHECK_EQ(to_chars_sub(buf, zpad(integral(nullptr, T(16)), 0u)), "0x0"); + CHECK_EQ(to_chars_sub(buf, zpad(integral(nullptr, T(2)), 0u)), "0b0"); + CHECK_EQ(to_chars_sub(buf, zpad(integral(nullptr, T(8)), 0u)), "0o0"); + CHECK_EQ(to_chars_sub(buf, zpad(hex(nullptr), 0u)), "0x0"); + CHECK_EQ(to_chars_sub(buf, zpad(bin(nullptr), 0u)), "0b0"); + CHECK_EQ(to_chars_sub(buf, zpad(oct(nullptr), 0u)), "0o0"); + CHECK_EQ(to_chars_sub(buf, zpad(integral(nullptr, T(10)), 5u)), "00000"); + CHECK_EQ(to_chars_sub(buf, zpad(integral(nullptr, T(16)), 5u)), "0x00000"); + CHECK_EQ(to_chars_sub(buf, zpad(integral(nullptr, T(2)), 5u)), "0b00000"); + CHECK_EQ(to_chars_sub(buf, zpad(integral(nullptr, T(8)), 5u)), "0o00000"); + CHECK_EQ(to_chars_sub(buf, zpad(hex(nullptr), 5u)), "0x00000"); + CHECK_EQ(to_chars_sub(buf, zpad(bin(nullptr), 5u)), "0b00000"); + CHECK_EQ(to_chars_sub(buf, zpad(oct(nullptr), 5u)), "0o00000"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T> +void test_to_chars_real(T f, int precision, const char* flt, T fltv, const char *scient, T scientv) +{ + char bufc[64]; + substr buf(bufc); + substr r; + T copy; + + INFO("num=" << f); + + r = to_chars_sub(buf, fmt::real(f, precision)); + CHECK_EQ(r, to_csubstr(flt)); + from_chars(r, ©); + if(sizeof(T) == sizeof(float)) + { + CHECK_FLOAT_EQ((float)fltv, (float)copy); + } + else + { + CHECK_FLOAT_EQ(fltv, copy); + } + + r = to_chars_sub(buf, fmt::real(f, precision, FTOA_SCIENT)); + CHECK_EQ(r, to_csubstr(scient)); + from_chars(r, ©); + if(sizeof(T) == sizeof(float)) + { + CHECK_FLOAT_EQ((float)scientv, (float)copy); + } + else + { + CHECK_FLOAT_EQ(scientv, copy); + } +} + +TEST_CASE_TEMPLATE("to_chars.fmt.real", T, float, double) +{ + char bufc[128]; + substr buf(bufc); + + T f = static_cast<T>(256.064); + test_to_chars_real<T>(f, 0, "256", T(256.), "3e+02", T(300.)); + test_to_chars_real<T>(f, 1, "256.1", T(256.1), "2.6e+02", T(260.)); + test_to_chars_real<T>(f, 2, "256.06", T(256.06), "2.56e+02", T(256.)); + test_to_chars_real<T>(f, 3, "256.064", T(256.064), "2.561e+02", T(256.1)); + test_to_chars_real<T>(f, 4, "256.0640", T(256.0640), "2.5606e+02", T(256.06)); + test_to_chars_real<T>(f, 5, "256.06400", T(256.06400), "2.56064e+02", T(256.064)); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE("to_chars.fmt.boolalpha") +{ + char bufc[128]; + substr buf(bufc); + + CHECK_EQ(to_chars_sub(buf, true), "1"); + CHECK_EQ(to_chars_sub(buf, false), "0"); + CHECK_EQ(to_chars_sub(buf, fmt::boolalpha(true)), "true"); + CHECK_EQ(to_chars_sub(buf, 1), "1"); + CHECK_EQ(to_chars_sub(buf, fmt::boolalpha(1)), "true"); + CHECK_EQ(to_chars_sub(buf, fmt::boolalpha(10)), "true"); + CHECK_EQ(to_chars_sub(buf, fmt::boolalpha(false)), "false"); + CHECK_EQ(to_chars_sub(buf, fmt::boolalpha(0)), "false"); + CHECK_EQ(to_chars_sub(buf, fmt::boolalpha(0u)), "false"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE("align.left.overflow") +{ + CHECK_EQ(to_chars(substr(), fmt::left(' ', 91u)), 91u); + CHECK_EQ(to_chars(substr(), fmt::left("0123456789.123456789.123456789.123456789", 91u)), 91u); + CHECK_EQ(to_chars(substr(), fmt::left("0123456789.123456789.123456789.123456789", 30u)), 40u); +} + +TEST_CASE("align.right.overflow") +{ + CHECK_EQ(to_chars(substr(), fmt::right(' ', 91u)), 91u); + CHECK_EQ(to_chars(substr(), fmt::right("0123456789.123456789.123456789.123456789", 91u)), 91u); + CHECK_EQ(to_chars(substr(), fmt::right("0123456789.123456789.123456789.123456789", 30u)), 40u); +} + +TEST_CASE("align.left") +{ + char buf[128] = {}; + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 1)), "1"); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 2)), "1 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 3)), "1 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 4)), "1 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 5)), "1 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 6)), "1 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 7)), "1 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 8)), "1 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 9)), "1 "); + + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 1, '+')), "1"); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 2, '+')), "1+"); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 3, '+')), "1++"); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 4, '+')), "1+++"); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 5, '+')), "1++++"); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 6, '+')), "1+++++"); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 7, '+')), "1++++++"); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 8, '+')), "1+++++++"); + CHECK_EQ(to_chars_sub(buf, fmt::left("1", 9, '+')), "1++++++++"); + + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 0)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 1)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 2)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 3)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 4)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 5)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 6)), "01234 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 7)), "01234 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 8)), "01234 "); + CHECK_EQ(to_chars_sub(buf, fmt::left("01234", 9)), "01234 "); + + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 0)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 1)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 2)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 3)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 4)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 5)), "1234 "); + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 6)), "1234 "); + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 7)), "1234 "); + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 8)), "1234 "); + CHECK_EQ(to_chars_sub(buf, fmt::left(1234, 9)), "1234 "); +} + + +TEST_CASE("align.right") +{ + char buf[128] = {}; + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 1)), "1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 2)), " 1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 3)), " 1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 4)), " 1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 5)), " 1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 6)), " 1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 7)), " 1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 8)), " 1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 9)), " 1"); + + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 1, '+')), "1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 2, '+')), "+1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 3, '+')), "++1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 4, '+')), "+++1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 5, '+')), "++++1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 6, '+')), "+++++1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 7, '+')), "++++++1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 8, '+')), "+++++++1"); + CHECK_EQ(to_chars_sub(buf, fmt::right("1", 9, '+')), "++++++++1"); + + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 0)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 1)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 2)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 3)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 4)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 5)), "01234"); + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 6)), " 01234"); + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 7)), " 01234"); + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 8)), " 01234"); + CHECK_EQ(to_chars_sub(buf, fmt::right("01234", 9)), " 01234"); + + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 0)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 1)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 2)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 3)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 4)), "1234"); + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 5)), " 1234"); + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 6)), " 1234"); + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 7)), " 1234"); + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 8)), " 1234"); + CHECK_EQ(to_chars_sub(buf, fmt::right(1234, 9)), " 1234"); + + CHECK_EQ(to_chars_sub(buf, fmt::real(0.124, 1)), "0.1"); // we assume this in what follows + CHECK_EQ(to_chars_sub(buf, fmt::real(0.124, 2)), "0.12"); + CHECK_EQ(to_chars_sub(buf, fmt::real(0.124, 3)), "0.124"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 1), 0)), "0.1"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 1), 1)), "0.1"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 1), 2)), "0.1"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 1), 3)), "0.1"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 1), 4)), " 0.1"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 1), 5)), " 0.1"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 1), 6)), " 0.1"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 1), 7)), " 0.1"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 2), 0)), "0.12"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 2), 1)), "0.12"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 2), 2)), "0.12"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 2), 3)), "0.12"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 2), 4)), "0.12"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 2), 5)), " 0.12"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 2), 6)), " 0.12"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 2), 7)), " 0.12"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 3), 0)), "0.124"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 3), 1)), "0.124"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 3), 2)), "0.124"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 3), 3)), "0.124"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 3), 4)), "0.124"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 3), 5)), "0.124"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 3), 6)), " 0.124"); + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(0.124, 3), 7)), " 0.124"); + + CHECK_EQ(to_chars_sub(buf, fmt::right(fmt::real(1234.5222, 1), 7)), " 1234.5"); + auto r = [](double val, size_t width) { return fmt::right(fmt::real(val, 1), width); }; + CHECK_EQ(to_chars_sub(buf, r(1234.5, 7)), " 1234.5"); + c4::format(buf, "freq={}Hz\0", r(1234.5, 7)); + CHECK_EQ(to_csubstr(buf).len, to_csubstr("freq= 1234.5Hz").len); + CHECK_EQ(to_csubstr(buf), "freq= 1234.5Hz"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE("cat.vars") +{ + char buf[256]; + substr sp(buf); + csubstr result; + size_t sz; + + sz = cat(buf, 1, ' ', 2, ' ', 3, ' ', 4); + result = sp.left_of(sz); + CHECK_EQ(result, "1 2 3 4"); +} + +#ifdef C4_TUPLE_TO_STR +TEST_CASE("cat.tuple") +{ + char buf[256]; + substr sp(buf); + csubstr result; + size_t sz; + + sz = cat(buf, std::forward_as_tuple(1, ' ', 2, ' ', 3, ' ', 4)); + result = sp.left_of(sz); + CHECK_EQ(result, "1 2 3 4"); +} +#endif // C4_TUPLE_TO_STR + +TEST_CASE("uncat.vars") +{ + size_t sz; + size_t npos = csubstr::npos; + int v1 = 0, v2 = 0, v3 = 0, v4 = 0; + + sz = uncat("1 2 3 4", v1, v2, v3, v4); + CHECK_NE(sz, npos); + CHECK_EQ(sz, 7); + CHECK_EQ(v1, 1); + CHECK_EQ(v2, 2); + CHECK_EQ(v3, 3); + CHECK_EQ(v4, 4); +} + +#ifdef C4_TUPLE_TO_STR +TEST_CASE("uncat.tuple") +{ + size_t sz; + int v1 = 0, v2 = 0, v3 = 0, v4 = 0; + + auto tp = std::forward_as_tuple(v1, v2, v3, v4); + sz = uncat("1 2 3 4", tp); + CHECK_NE(sz, csubstr::npos); + CHECK_EQ(sz, 7); + CHECK_EQ(v1, 1); + CHECK_EQ(v2, 2); + CHECK_EQ(v3, 3); + CHECK_EQ(v4, 4); +} +#endif // C4_TUPLE_TO_STR + + +TEST_CASE("catsep.vars") +{ + char buf[256]; + substr sp(buf); + csubstr result; + size_t sz; + + sz = catsep(buf, ' ', 1, 2); + CHECK_EQ(sz, 3); + result = sp.left_of(sz); + CHECK_EQ(result, "1 2"); + + sz = catsep(buf, '/', 1, 2); + CHECK_EQ(sz, 3); + result = sp.left_of(sz); + CHECK_EQ(result, "1/2"); + + sz = catsep(buf, ' ', 1, 2, 3, 4); + CHECK_EQ(sz, 7); + result = sp.left_of(sz); + CHECK_EQ(result, "1 2 3 4"); + + sz = catsep(buf, '/', 1, 2, 3, 4); + CHECK_EQ(sz, 7); + result = sp.left_of(sz); + CHECK_EQ(result, "1/2/3/4"); +} + +#ifdef C4_TUPLE_TO_STR +TEST_CASE("catsep.tuple") +{ + char buf[256]; + substr sp(buf); + csubstr result; + size_t sz; + + sz = catsep(buf, ' ', std::forward_as_tuple(1, 2)); + CHECK_EQ(sz, 3); + result = sp.left_of(sz); + CHECK_EQ(result, "1 2"); + + sz = catsep(buf, '/', std::forward_as_tuple(1, 2)); + CHECK_EQ(sz, 3); + result = sp.left_of(sz); + CHECK_EQ(result, "1/2"); + + sz = catsep(buf, ' ', std::forward_as_tuple(1, 2, 3, 4)); + CHECK_EQ(sz, 7); + result = sp.left_of(sz); + CHECK_EQ(result, "1 2 3 4"); + + sz = catsep(buf, '/', std::forward_as_tuple(1, 2, 3, 4)); + CHECK_EQ(sz, 7); + result = sp.left_of(sz); + CHECK_EQ(result, "1/2/3/4"); +} +#endif // C4_TUPLE_TO_STR + +TEST_CASE("uncatsep.vars") +{ + size_t sz; + int v1 = 0, v2 = 0, v3 = 0, v4 = 0; + char sep; + + sz = uncatsep("1 2 3 4", sep, v1, v2, v3, v4); + CHECK_EQ(sz, 7); + CHECK_EQ(v1, 1); + CHECK_EQ(v2, 2); + CHECK_EQ(v3, 3); + CHECK_EQ(v4, 4); +} + +#ifdef C4_TUPLE_TO_STR +TEST_CASE("uncatsep.tuple") +{ + size_t sz; + int v1 = 0, v2 = 0, v3 = 0, v4 = 0; + char sep; + + auto tp = std::forward_as_tuple(v1, v2, v3, v4); + sz = uncatsep("1 2 3 4", sep, tp); + CHECK_EQ(sz, 7); + CHECK_EQ(v1, 1); + CHECK_EQ(v2, 2); + CHECK_EQ(v3, 3); + CHECK_EQ(v4, 4); +} +#endif // C4_TUPLE_TO_STR + +TEST_CASE("format.vars") +{ + char buf[256]; + substr sp(buf); + csubstr result; + size_t sz; + + sz = format(buf, "{} and {} and {} and {}", 1, 2, 3, 4); + CHECK_EQ(sz, strlen("1 and 2 and 3 and 4")); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and 2 and 3 and 4"); + + sz = format(buf, "{} and {} and {} and {}", 1, 2, 3, 4, 5, 6, 7); + CHECK_EQ(sz, 19); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and 2 and 3 and 4"); + + sz = format(buf, "{} and {} and {} and {}", 1, 2, 3); + CHECK_EQ(sz, 20); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and 2 and 3 and {}"); + + sz = format(buf, "{} and {} and {} and {}", 1, 2); + CHECK_EQ(sz, 21); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and 2 and {} and {}"); + + sz = format(buf, "{} and {} and {} and {}", 1); + CHECK_EQ(sz, 22); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and {} and {} and {}"); + + sz = format(buf, "{} and {} and {} and {}"); + CHECK_EQ(sz, 23); + result = sp.left_of(sz); + CHECK_EQ(result, "{} and {} and {} and {}"); + + sz = format(buf, "{} args only at the begin", 1); + CHECK_EQ(sz, csubstr("1 args only at the begin").len); + result = sp.left_of(sz); + CHECK_EQ(result, csubstr("1 args only at the begin")); +} + +TEST_CASE("format.empty_buffer") +{ + size_t sz = format({}, "{} and {} and {} and {}", 1, 2, 3, 4); + CHECK_EQ(sz, strlen("1 and 2 and 3 and 4")); + char buf_[128]; + substr buf = buf_; + sz = format(buf, "{} and {} and {} and {}", 1, 2, 3, 4); + CHECK_EQ(sz, strlen("1 and 2 and 3 and 4")); + CHECK_EQ(format(buf, "{} and {} and {} and {}", 1, 2, 3, 4), + format({} , "{} and {} and {} and {}", 1, 2, 3, 4)); + CHECK_EQ(to_chars({}, 101), to_chars(buf, 101)); // eq for all integers + CHECK_GE(to_chars({}, 0.1f), to_chars(buf, 0.1f)); // ge for all floats, due to a sprintf quirk + CHECK_EQ(format(buf, "a={} foo {} {} bar {}", 101, 10, 11, 12), + format({} , "a={} foo {} {} bar {}", 101, 10, 11, 12)); +} + +#ifdef C4_TUPLE_TO_STR +TEST_CASE("format.tuple") +{ + char buf[256]; + substr sp(buf); + csubstr result; + size_t sz; + + sz = format(buf, "{} and {} and {} and {}", std::forward_as_tuple(1, 2, 3, 4)); + CHECK_EQ(sz, 19); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and 2 and 3 and 4"); + + sz = format(buf, "{} and {} and {} and {}", std::forward_as_tuple(1, 2, 3, 4, 5, 6, 7)); + CHECK_EQ(sz, 19); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and 2 and 3 and 4"); + + sz = format(buf, "{} and {} and {} and {}", std::forward_as_tuple(1, 2, 3)); + CHECK_EQ(sz, 20); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and 2 and 3 and {}"); + + sz = format(buf, "{} and {} and {} and {}", std::forward_as_tuple(1, 2)); + CHECK_EQ(sz, 21); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and 2 and {} and {}"); + + sz = format(buf, "{} and {} and {} and {}", std::forward_as_tuple(1)); + CHECK_EQ(sz, 22); + result = sp.left_of(sz); + CHECK_EQ(result, "1 and {} and {} and {}"); + + sz = format(buf, "{} and {} and {} and {}"); + CHECK_EQ(sz, 23); + result = sp.left_of(sz); + CHECK_EQ(result, "{} and {} and {} and {}"); +} +#endif // C4_TUPLE_TO_STR + +TEST_CASE("unformat.vars") +{ + size_t sz; + int v1 = 0, v2 = 0, v3 = 0, v4 = 0; + + sz = unformat("1 and 2 and 3 and 4", "{} and {} and {} and {}", v1, v2, v3, v4); + CHECK_EQ(sz, 19); + CHECK_EQ(v1, 1); + CHECK_EQ(v2, 2); + CHECK_EQ(v3, 3); + CHECK_EQ(v4, 4); + + v1 = 0; + sz = unformat("1 and 2 and 3 and 4" , "3", v1); + CHECK_EQ(sz, 1); + CHECK_EQ(v1, 0); + + v1 = 0; + sz = unformat("1,2,3,,,", "{},{},{}", v1, v2, v3); + CHECK_EQ(sz, 5); + CHECK_EQ(v1, 1); + CHECK_EQ(v2, 2); + CHECK_EQ(v3, 3); + + v1 = v2 = v3 = 0; + sz = unformat("1,2,3,,,", "{},{},{},,,", v1, v2, v3); + CHECK_EQ(sz, 8); // make sure we count the trailing characters in the format + CHECK_EQ(v1, 1); + CHECK_EQ(v2, 2); + CHECK_EQ(v3, 3); +} + +#ifdef C4_TUPLE_TO_STR +TEST_CASE("unformat.tuple") +{ + size_t sz; + int v1 = 0, v2 = 0, v3 = 0, v4 = 0; + + auto tp = std::forward_as_tuple(v1, v2, v3, v4); + sz = unformat("1 and 2 and 3 and 4", "{} and {} and {} and {}", tp); + CHECK_EQ(sz, 19); + CHECK_EQ(v1, 1); + CHECK_EQ(v2, 2); + CHECK_EQ(v3, 3); + CHECK_EQ(v4, 4); +} +#endif // C4_TUPLE_TO_STR + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +TEST_CASE("catrs.basic") +{ + std::vector<char> buf; + + catrs(&buf); + CHECK_EQ(to_csubstr(buf), ""); + + catrs(&buf, 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1234"); + catrs(&buf, 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5678"); +} + +TEST_CASE("catrs.basic_return") +{ + auto bufv = catrs<std::vector<char>>(9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + CHECK_EQ(to_csubstr(bufv), "9876543210"); + bufv = catrs<std::vector<char>>(); + CHECK_EQ(to_csubstr(bufv), ""); + CHECK(bufv.empty()); + + auto bufs = catrs<std::string>(9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + CHECK_EQ(to_csubstr(bufs), "9876543210"); +} + +TEST_CASE("catrs.basic_append") +{ + std::vector<char> buf; + + catrs(append, &buf); + CHECK_EQ(to_csubstr(buf), ""); + + catrs(append, &buf, 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1234"); + catrs(append, &buf, 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "12345678"); + catrs(append, &buf, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "123456789012345678"); +} + +template<class... Args> +void catrs_perfect_fwd(Args && ...args) +{ + catrs(std::forward<Args>(args)...); +} + +TEST_CASE("catrs.perfect_fwd") +{ + std::vector<char> buf; + catrs_perfect_fwd(&buf, 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1234"); + catrs_perfect_fwd(&buf, 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5678"); +} + +template<class... Args> +void catrs_const_fwd(Args const& ...args) +{ + catrs(args...); +} + +TEST_CASE("catrs.const_fwd") +{ + std::vector<char> buf; + catrs_const_fwd(&buf, 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1234"); + catrs_const_fwd(&buf, 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5678"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE("catseprs.basic") +{ + std::vector<char> buf; + + catseprs(&buf, ' '); + CHECK_EQ(to_csubstr(buf), ""); + + catseprs(&buf, ' ', 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1 2 3 4"); + catseprs(&buf, ' ', 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5 6 7 8"); + + catseprs(&buf, ',', 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1,2,3,4"); + catseprs(&buf, ',', 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5,6,7,8"); + + catseprs(&buf, '/', 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1/2/3/4"); + catseprs(&buf, '/', 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5/6/7/8"); + + catseprs(&buf, "///", 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1///2///3///4"); + catseprs(&buf, "///", 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5///6///7///8"); + + catseprs(&buf, 5678, 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1567825678356784"); + catseprs(&buf, 1234, 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5123461234712348"); +} + +TEST_CASE("catseprs.basic_return") +{ + auto bufv = catseprs<std::vector<char>>('a', 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + CHECK_EQ(to_csubstr(bufv), "9a8a7a6a5a4a3a2a1a0"); + + auto bufs = catseprs<std::string >('a', 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + CHECK_EQ(to_csubstr(bufs), "9a8a7a6a5a4a3a2a1a0"); +} + +TEST_CASE("catseprs.basic_append") +{ + std::vector<char> buf; + + auto ret = catseprs(append, &buf, ' '); + CHECK_EQ(to_csubstr(buf), ""); + CHECK_EQ(ret, ""); + + ret = catseprs(append, &buf, ' ', 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1 2 3 4"); + CHECK_EQ(ret, "1 2 3 4"); + ret = catseprs(append, &buf, ' ', 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "1 2 3 45 6 7 8"); + CHECK_EQ(ret, "5 6 7 8"); + ret = catseprs(append, &buf, ' ', 9, 0, 1, 2, 3, 4, 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "1 2 3 45 6 7 89 0 1 2 3 4 5 6 7 8"); + CHECK_EQ(ret, "9 0 1 2 3 4 5 6 7 8"); + + ret = catseprs(append, &buf, ' '); + CHECK_EQ(to_csubstr(buf), "1 2 3 45 6 7 89 0 1 2 3 4 5 6 7 8"); + CHECK_EQ(ret, ""); +} + +template<class... Args> +void catseprs_perfect_fwd(Args && ...args) +{ + catseprs(std::forward<Args>(args)...); +} + +template<class... Args> +void catseprs_const_fwd(Args const& ...args) +{ + catseprs(args...); +} + +TEST_CASE("catseprs.perfect_fwd") +{ + std::vector<char> buf; + catseprs_perfect_fwd(&buf, '.', 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1.2.3.4"); + catseprs_perfect_fwd(&buf, 0, 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5060708"); +} + +TEST_CASE("catseprs.const_fwd") +{ + std::vector<char> buf; + catseprs_const_fwd(&buf, '.', 1, 2, 3, 4); + CHECK_EQ(to_csubstr(buf), "1.2.3.4"); + catseprs_const_fwd(&buf, 0, 5, 6, 7, 8); + CHECK_EQ(to_csubstr(buf), "5060708"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TEST_CASE("formatrs.basic") +{ + std::vector<char> buf; + + formatrs(&buf, ""); + CHECK(buf.empty()); + + formatrs(&buf, "{} goes with food, {} goes with heat, {} anytime", "wine", "beer", "coffee"); + CHECK_EQ(to_csubstr(buf), "wine goes with food, beer goes with heat, coffee anytime"); + + formatrs(&buf, ""); + CHECK(buf.empty()); +} + +TEST_CASE("formatrs.basic_return") +{ + auto bufv = formatrs<std::vector<char>>("{} goes with food, {} goes with heat, {} anytime", "wine", "beer", "coffee"); + CHECK_EQ(to_csubstr(bufv), "wine goes with food, beer goes with heat, coffee anytime"); + + auto bufs = formatrs<std::string>("{} goes with food, {} goes with heat, {} anytime", "wine", "beer", "coffee"); + CHECK_EQ(to_csubstr(bufs), "wine goes with food, beer goes with heat, coffee anytime"); +} + +TEST_CASE("formatrs.basic_append") +{ + std::vector<char> buf; + + formatrs(append, &buf, "{} goes with food", "wine"); + CHECK_EQ(to_csubstr(buf), "wine goes with food"); + formatrs(append, &buf, ", {} goes with heat", "beer"); + CHECK_EQ(to_csubstr(buf), "wine goes with food, beer goes with heat"); + formatrs(append, &buf, ", {} anytime", "coffee"); + CHECK_EQ(to_csubstr(buf), "wine goes with food, beer goes with heat, coffee anytime"); + + formatrs(append, &buf, ". And water. {} glass of {}cl in the morning clears you up for the day", 1, 40); + CHECK_EQ(to_csubstr(buf), "wine goes with food, beer goes with heat, coffee anytime. And water. 1 glass of 40cl in the morning clears you up for the day"); +} + +template<class... Args> +void formatrs_perfect_fwd(Args && ...args) +{ + formatrs(std::forward<Args>(args)...); +} + +template<class... Args> +void formatrs_const_fwd(Args const& ...args) +{ + formatrs(args...); +} + +TEST_CASE("formatrs.perfect_fwd") +{ + std::vector<char> buf; + formatrs_perfect_fwd(&buf, "Too much of anything is bad, but too much {} is {}.", "Champagne", "just right"); + CHECK_EQ(to_csubstr(buf), "Too much of anything is bad, but too much Champagne is just right."); + formatrs_perfect_fwd(&buf, "{}, I am tasting the {}", "Come quickly", "stars!"); + CHECK_EQ(to_csubstr(buf), "Come quickly, I am tasting the stars!"); + formatrs_perfect_fwd(&buf, "{} the only wine that leaves a {} {} after {}.", "Champagne is", "woman", "beautiful", "drinking it"); + CHECK_EQ(to_csubstr(buf), "Champagne is the only wine that leaves a woman beautiful after drinking it."); + formatrs_perfect_fwd(&buf, "Remember {}, it's not just {} we are fighting for, it's {}", "gentlemen", "France", "Champagne!"); + CHECK_EQ(to_csubstr(buf), "Remember gentlemen, it's not just France we are fighting for, it's Champagne!"); + // https://www.townandcountrymag.com/leisure/drinks/how-to/g828/the-10-best-quotes-about-champagne/ +} + +TEST_CASE("formatrs.const_fwd") +{ + std::vector<char> buf; + formatrs_const_fwd(&buf, "Too much of anything is bad, but too much {} is {}.", "Champagne", "just right"); + CHECK_EQ(to_csubstr(buf), "Too much of anything is bad, but too much Champagne is just right."); + formatrs_const_fwd(&buf, "{}, I am tasting the {}", "Come quickly", "stars!"); + CHECK_EQ(to_csubstr(buf), "Come quickly, I am tasting the stars!"); + formatrs_const_fwd(&buf, "{} the only wine that leaves a {} {} after {}.", "Champagne is", "woman", "beautiful", "drinking it"); + CHECK_EQ(to_csubstr(buf), "Champagne is the only wine that leaves a woman beautiful after drinking it."); + formatrs_const_fwd(&buf, "Remember {}, it's not just {} we are fighting for, it's {}", "gentlemen", "France", "Champagne!"); + CHECK_EQ(to_csubstr(buf), "Remember gentlemen, it's not just France we are fighting for, it's Champagne!"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T> +void test_hex(T in, csubstr expected) +{ + INFO("expected=" << expected); + SUBCASE("charbuf") + { + char buf_[128] = {}; + substr buf = buf_; + buf.fill('?'); // this will be executed before each case + SUBCASE("sz=0") + { + CHECK_EQ(cat(buf.first(0), fmt::hex(in)), expected.len); + CHECK_EQ(buf[0], '?'); + CHECK_EQ(buf.trimr('?'), ""); + } + SUBCASE("sz=1") + { + CHECK_EQ(cat(buf.first(1), fmt::hex(in)), expected.len); + CHECK_EQ(buf[0], '?'); + CHECK_EQ(buf.trimr('?'), ""); + } + SUBCASE("sz=2") + { + CHECK_EQ(cat(buf.first(2), fmt::hex(in)), expected.len); + CHECK_EQ(buf[0], '?'); + CHECK_EQ(buf.trimr('?'), ""); + } + SUBCASE("full") + { + REQUIRE_EQ(cat(buf, fmt::hex(in)), expected.len); + CHECK_EQ(buf.first(expected.len), expected); + CHECK_EQ(buf.trimr('?'), expected); + } + } + SUBCASE("vector") + { + std::vector<char> buf; + catrs(&buf, fmt::hex(in)); + CHECK_EQ(buf.size(), expected.len); + CHECK_EQ(to_csubstr(buf), expected); + } +} + +TEST_CASE("fmt.hex") +{ + test_hex(0, "0x0"); + test_hex(nullptr, "0x0"); + test_hex(254, "0xfe"); + test_hex(255, "0xff"); + test_hex(256, "0x100"); +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T> void test_raw_roundtrip(const char *valstr, T const& orig) +{ + INFO("valstr=" << valstr); + alignas(alignof(T)) char buf_[2 * (sizeof(T) + alignof(T))] = {}; + substr buf = buf_; + + fmt::const_raw_wrapper rawwrap = fmt::raw(orig); + REQUIRE_EQ((void*)rawwrap.buf, (void*)&orig); + REQUIRE_EQ(rawwrap.len, sizeof(orig)); + + for(size_t i = 0; i < alignof(T); ++i) + { + INFO(" i=" << i); + // make sure to cover unaligned buffers + substr sbuf = buf.sub(i); + size_t szwrite = c4::to_chars(sbuf, fmt::raw(orig)); + REQUIRE_LE(szwrite, sbuf.len); + if(i == 0) + { + REQUIRE_EQ(szwrite, sizeof(T)); + } + else + { + REQUIRE_GT(szwrite, sizeof(T)); + } + T copy = {}; + REQUIRE_NE(copy, orig); + bool ok = c4::from_chars_first(sbuf, fmt::raw(copy)); + REQUIRE_EQ(ok, true); + CHECK_EQ(copy, orig); + + // cover also insufficient buffers + sbuf = sbuf.first(sizeof(T)-1); + memset(buf.str, 0, buf.len); + szwrite = c4::to_chars(sbuf, fmt::raw(orig)); + REQUIRE_GT(szwrite, sbuf.len); + for(char c : buf) + { + CHECK_EQ(c, 0); + } + } +} + +TEST_CASE("fmt.raw_int") +{ + #define _(v) test_raw_roundtrip(#v, v) + + _(int(1)); + _(int(2)); + _(int(-1)); + _(int(-2)); + + #undef _ +} + +} // namespace c4 + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_log.cpp b/thirdparty/ryml/ext/c4core/test/test_log.cpp new file mode 100644 index 000000000..2f5b56734 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_log.cpp @@ -0,0 +1,70 @@ +#include "c4/log.hpp" + +#include "c4/libtest/supprwarn_push.hpp" +#include "c4/test.hpp" + +namespace c4 { + +TEST(LogBuffer, basic) +{ +#define _CHECK(s, str) \ + EXPECT_EQ(strncmp(s.rd(), str, s.pos), 0) << " string was '" << s.rd() << "'"; \ + s.clear(); \ + EXPECT_EQ(s.pos, 0);\ + EXPECT_EQ(s.buf[0], '\0') + + LogBuffer b; + const char *foo = "Foo"; + const char *bars_str = "123"; + int bars = 123; + + // raw writing + b.write("hello world I am "); + b.write(foo); + b.write(" and I frobnicate "); + b.write(bars_str); // only accepts const char* + b.write(" Bars"); + _CHECK(b, "hello world I am Foo and I frobnicate 123 Bars"); + + // chevron-style AKA iostream-style + b << "hello world I am " << foo << " and I frobnicate " << bars << " Bars"; + _CHECK(b, "hello world I am Foo and I frobnicate 123 Bars"); + + // c-style, not type safe + b.printf("hello world I am %s and I frobnicate %d Bars", foo, bars); + _CHECK(b, "hello world I am Foo and I frobnicate 123 Bars"); + + // python-style, type safe + b.print("hello world I am {} and I frobnicate {} Bars", foo, bars); + _CHECK(b, "hello world I am Foo and I frobnicate 123 Bars"); + + // r-style, type safe + b.cat("hello world I am ", foo, " and I frobnicate ", bars, " Bars"); + _CHECK(b, "hello world I am Foo and I frobnicate 123 Bars"); + + // using separators: this is unpractical... + const char *s[] = {"now", "we", "have", "11", "strings", "to", "cat", "one", "after", "the", "other"}; + b.cat(s[0], ' ', s[1], ' ', s[2], ' ', s[3], ' ', s[4], ' ', + s[5], ' ', s[6], ' ', s[7], ' ', s[8], ' ', s[9], ' ', s[10]); + _CHECK(b, "now we have 11 strings to cat one after the other"); + + // ... and this resolves it + b.catsep(' ', s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10]); + _CHECK(b, "now we have 11 strings to cat one after the other"); + + b.catsep('_', s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10]); + _CHECK(b, "now_we_have_11_strings_to_cat_one_after_the_other"); + + // can be a full string + b.catsep("____", s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10]); + _CHECK(b, "now____we____have____11____strings____to____cat____one____after____the____other"); + + // or just a general object + b.catsep(22, s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10]); + _CHECK(b, "now22we22have221122strings22to22cat22one22after22the22other"); + +} + +} // namespace c4 + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_memory_resource.cpp b/thirdparty/ryml/ext/c4core/test/test_memory_resource.cpp new file mode 100644 index 000000000..7ae7a71ac --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_memory_resource.cpp @@ -0,0 +1,255 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/memory_resource.hpp" +#endif + +#include "c4/libtest/supprwarn_push.hpp" + +#include <limits> + +#include "c4/test.hpp" + +namespace c4 { + +TEST_CASE("set_aalloc.basic") +{ + auto a = get_aalloc(); + set_aalloc(nullptr); + CHECK_EQ(get_aalloc(), nullptr); + set_aalloc(a); + CHECK_EQ(get_aalloc(), a); +} + +TEST_CASE("set_afree.basic") +{ + auto a = get_afree(); + set_afree(nullptr); + CHECK_EQ(get_afree(), nullptr); + set_afree(a); + CHECK_EQ(get_afree(), a); +} + +TEST_CASE("set_arealloc.basic") +{ + auto a = get_arealloc(); + set_arealloc(nullptr); + CHECK_EQ(get_arealloc(), nullptr); + set_arealloc(a); + CHECK_EQ(get_arealloc(), a); +} + +//----------------------------------------------------------------------------- +namespace detail { +void* aalloc_impl(size_t size, size_t alignment=alignof(max_align_t)); +void* arealloc_impl(void *ptr, size_t oldsz, size_t newsz, size_t alignment=alignof(max_align_t)); +void afree_impl(void *ptr); +} // namespace detail + +TEST_CASE("aalloc_impl.error_bad_align") +{ +#if defined(C4_POSIX) + C4_EXPECT_ERROR_OCCURS(1); + auto *mem = detail::aalloc_impl(64, 9); // allocating with a non-power of two value is invalid + CHECK_EQ(mem, nullptr); +#endif +} + +TEST_CASE("aalloc_impl.error_out_of_mem") +{ +#if defined(C4_POSIX) + if(sizeof(size_t) != 8) return; // valgrind complains that size is -1 + C4_EXPECT_ERROR_OCCURS(1); + size_t sz = std::numeric_limits<size_t>::max(); + sz /= 2; + auto *mem = detail::aalloc_impl(sz); + CHECK_EQ(mem, nullptr); +#endif +} + +//----------------------------------------------------------------------------- + +void do_test_realloc(arealloc_pfn fn) +{ +#define _set(dim) for(uint8_t i = 0; i < dim; ++i) { mem[i] = static_cast<char>(i); } +#define _check(dim) for(uint8_t i = 0; i < dim; ++i) { CHECK_EQ(mem[i], i); } + + char *mem = (char*) aalloc(16, alignof(max_align_t)); + _set(16u); + _check(16u); + mem = (char*) fn(mem, 16, 20, alignof(max_align_t)); + _check(16u); + mem = (char*) fn(mem, 8, 20, alignof(max_align_t)); + _check(8u); + afree(mem); + +#undef _set +#undef _check +} + +TEST_CASE("realloc_impl.basic") +{ + do_test_realloc(&detail::arealloc_impl); +} + +TEST_CASE("realloc.basic") +{ + do_test_realloc(&arealloc); +} + + +//----------------------------------------------------------------------------- + +void do_memreslinear_realloc_test(MemoryResourceLinear &mr) +{ + C4_ASSERT(mr.capacity() >= 128); // this is needed for the tests below + + char * mem = (char*) mr.allocate(32); + CHECK_EQ(mem-(char*)mr.mem(), 0); + CHECK_EQ(mr.size(), 32); + CHECK_EQ(mr.slack(), mr.capacity() - 32); + + mem = (char*) mr.reallocate(mem, 32, 16); + CHECK_EQ(mem-(char*)mr.mem(), 0); + CHECK_EQ(mr.size(), 16); + CHECK_EQ(mr.slack(), mr.capacity() - 16); + + mem = (char*) mr.reallocate(mem, 16, 64); + CHECK_EQ(mem-(char*)mr.mem(), 0); + CHECK_EQ(mr.size(), 64); + CHECK_EQ(mr.slack(), mr.capacity() - 64); + + mem = (char*) mr.reallocate(mem, 64, 32); + CHECK_EQ(mem-(char*)mr.mem(), 0); + CHECK_EQ(mr.size(), 32); + CHECK_EQ(mr.slack(), mr.capacity() - 32); + + + char *mem2 = (char*) mr.allocate(32); + CHECK_EQ(mem-(char*)mr.mem(), 0); + CHECK_EQ(mem2-(char*)mr.mem(), 32); + CHECK_EQ(mr.size(), 64); + CHECK_EQ(mr.slack(), mr.capacity() - 64); + + mem = (char*) mr.reallocate(mem, 32, 16); + CHECK_EQ(mem-(char*)mr.mem(), 0); + CHECK_EQ(mr.size(), 64); + CHECK_EQ(mr.slack(), mr.capacity() - 64); +} + +TEST_CASE("MemoryResourceLinear.reallocate") +{ + MemoryResourceLinear mr(128); + do_memreslinear_realloc_test(mr); +} + +TEST_CASE("MemoryResourceLinearArr.reallocate") +{ + MemoryResourceLinearArr<128> mr; + do_memreslinear_realloc_test(mr); +} + + +//----------------------------------------------------------------------------- + +TEST_CASE("MemoryResourceLinear.error_out_of_mem") +{ + { + C4_EXPECT_ERROR_OCCURS(0); + MemoryResourceLinear mr(8); + mr.allocate(2); + } + + { + C4_EXPECT_ERROR_OCCURS(2); + MemoryResourceLinear mr(8); + mr.allocate(9); + } +} + +TEST_CASE("MemoryResourceLinearArr.error_out_of_mem") +{ + { + C4_EXPECT_ERROR_OCCURS(0); + MemoryResourceLinearArr<8> mr; + mr.allocate(2); + } + + { + C4_EXPECT_ERROR_OCCURS(2); + MemoryResourceLinearArr<8> mr; + mr.allocate(9); + } +} + + +//----------------------------------------------------------------------------- + +TEST_CASE("ScopedMemoryResource.basic") +{ + auto *before = get_memory_resource(); + { + MemoryResourceCounts mrc; + ScopedMemoryResource smr(&mrc); + CHECK_EQ(get_memory_resource(), &mrc); + } + CHECK_EQ(get_memory_resource(), before); +} + +TEST_CASE("ScopedMemoryResourceCounts.basic") +{ + auto *before = get_memory_resource(); + { + auto sac = ScopedMemoryResourceCounts{}; + CHECK_EQ(get_memory_resource(), &sac.mr); + } + CHECK_EQ(get_memory_resource(), before); +} + +TEST_CASE("ScopedMemoryResourceCounts.counts") +{ + auto *before = get_memory_resource(); + C4_UNUSED(before); + + { + auto checker = AllocationCountsChecker(); + auto *mr = &checker.mr; + + for(size_t sz : {16u, 32u, 64u, 128u}) + { + void *mem = mr->allocate(sz); + checker.check_all_delta(1, static_cast<ssize_t>(sz), static_cast<ssize_t>(sz)); + mr->deallocate(mem, sz); + checker.reset(); + } + checker.check_curr(0, 0); + checker.check_total(4, 240); + checker.check_max(1, 128); + } + + { + auto checker = AllocationCountsChecker(); + auto *mr = &checker.mr; + + std::pair<void *, size_t> mem[4] = {{0,16}, {0,32}, {0,64}, {0,128}}; + for(auto& m : mem) + { + m.first = mr->allocate(m.second); + checker.check_curr_delta(1, static_cast<ssize_t>(m.second)); + checker.reset(); + } + checker.check_curr(4, 240); + checker.check_total(4, 240); + checker.check_max(4, 240); + for(auto& m : mem) + { + mr->deallocate(m.first, m.second); + } + checker.check_curr(0, 0); + checker.check_total(4, 240); + checker.check_max(4, 240); + } + +} + +} // namespace c4 + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_memory_util.cpp b/thirdparty/ryml/ext/c4core/test/test_memory_util.cpp new file mode 100644 index 000000000..36509070c --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_memory_util.cpp @@ -0,0 +1,415 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/memory_util.hpp" +#endif + +#include "c4/libtest/supprwarn_push.hpp" + +#include <c4/test.hpp> +#include <limits> + +namespace c4 { + +TEST_CASE("mem_overlaps") +{ + csubstr buf = "0123456789012345678901234567890123456789"; + CHECK_EQ(buf.len, 40); + auto overlaps = [](csubstr lhs, csubstr rhs){ + bool res = mem_overlaps(lhs.str, rhs.str, lhs.len, rhs.len); + CHECK(res == lhs.overlaps(rhs)); + return res; + }; + CHECK(!overlaps(buf.first(0), buf.last(0))); + CHECK(!overlaps(buf.first(5), buf.last(5))); + CHECK(!overlaps(buf.first(10), buf.last(10))); + CHECK(!overlaps(buf.first(20), buf.last(20))); + CHECK(overlaps(buf.first(21), buf.last(20))); + CHECK(overlaps(buf.first(20), buf.last(21))); + CHECK(!overlaps(buf.first(0), buf)); + CHECK(overlaps(buf.first(1), buf)); + CHECK(!overlaps(buf, buf.last(0))); + CHECK(overlaps(buf, buf.last(1))); + CHECK(!overlaps(buf.first(20), buf.last(20))); + CHECK(overlaps(buf.first(21), buf.last(20))); + CHECK(overlaps(buf.first(20), buf.first(21))); +} + +TEST_CASE("mem_repeatT.one_repetition") +{ + char buf[120] = {0}; + + mem_repeat(buf, "123", 2, 1); + CHECK_EQ(strcmp(buf, "12"), 0); + + mem_repeat(buf, "123", 3, 1); + CHECK_EQ(strcmp(buf, "123"), 0); +} + +TEST_CASE("mem_repeatT.basic") +{ + char buf[120] = {0}; + + mem_zero(buf); + + mem_repeat(buf, "123", 2, 2); + CHECK_EQ(strcmp(buf, "1212"), 0); + CHECK_EQ(buf[4], '\0'); + + mem_zero(buf); + + mem_repeat(buf, "123", 3, 2); + CHECK_EQ(strcmp(buf, "123123"), 0); + CHECK_EQ(buf[6], '\0'); + + mem_zero(buf); + + mem_repeat(buf, "123", 2, 3); + CHECK_EQ(strcmp(buf, "121212"), 0); + CHECK_EQ(buf[6], '\0'); + + mem_zero(buf, sizeof(buf)); + + mem_repeat(buf, "123", 3, 3); + CHECK_EQ(strcmp(buf, "123123123"), 0); + CHECK_EQ(buf[9], '\0'); + + mem_zero(buf, sizeof(buf)); + + mem_repeat(buf, "123", 2, 4); + CHECK_EQ(strcmp(buf, "12121212"), 0); + CHECK_EQ(buf[8], '\0'); + + mem_zero(buf, sizeof(buf)); + + mem_repeat(buf, "123", 3, 4); + CHECK_EQ(strcmp(buf, "123123123123"), 0); + CHECK_EQ(buf[12], '\0'); + + mem_zero(buf, sizeof(buf)); + + mem_repeat(buf, "123", 2, 5); + CHECK_EQ(strcmp(buf, "1212121212"), 0); + CHECK_EQ(buf[10], '\0'); + + mem_zero(buf, sizeof(buf)); + + mem_repeat(buf, "123", 3, 5); + CHECK_EQ(strcmp(buf, "123123123123123"), 0); + CHECK_EQ(buf[15], '\0'); + + mem_zero(buf, sizeof(buf)); + + mem_repeat(buf, "123", 2, 6); + CHECK_EQ(strcmp(buf, "121212121212"), 0); + CHECK_EQ(buf[12], '\0'); + + mem_zero(buf, sizeof(buf)); + + mem_repeat(buf, "123", 3, 6); + CHECK_EQ(strcmp(buf, "123123123123123123"), 0); + CHECK_EQ(buf[18], '\0'); +} + + +//----------------------------------------------------------------------------- + +TEST_CASE("is_aligned.basic") +{ + CHECK(is_aligned<int>((int*)0x0)); + CHECK_FALSE(is_aligned<int>((int*)0x1)); + CHECK_FALSE(is_aligned<int>((int*)0x2)); + CHECK_FALSE(is_aligned<int>((int*)0x3)); + CHECK_FALSE(is_aligned<int>((int*)0x3)); + CHECK(is_aligned<int>((int*)0x4)); +} + + +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("lsb.basic", T, uint8_t, uint16_t, uint32_t, uint64_t) +{ + //CHECK_EQ(lsb<T>( 0), T(0)); + CHECK_EQ(lsb<T>( 1), T(0)); + CHECK_EQ(lsb<T>( 2), T(1)); + CHECK_EQ(lsb<T>( 3), T(0)); + CHECK_EQ(lsb<T>( 4), T(2)); + CHECK_EQ(lsb<T>( 5), T(0)); + CHECK_EQ(lsb<T>( 6), T(1)); + CHECK_EQ(lsb<T>( 7), T(0)); + CHECK_EQ(lsb<T>( 8), T(3)); + CHECK_EQ(lsb<T>( 9), T(0)); + CHECK_EQ(lsb<T>(10), T(1)); + CHECK_EQ(lsb<T>(11), T(0)); + CHECK_EQ(lsb<T>(12), T(2)); + CHECK_EQ(lsb<T>(13), T(0)); + CHECK_EQ(lsb<T>(14), T(1)); + CHECK_EQ(lsb<T>(15), T(0)); + CHECK_EQ(lsb<T>(16), T(4)); +} + +TEST_CASE_TEMPLATE("lsb11.basic", T, uint8_t, uint16_t, uint32_t, uint64_t) +{ + //CHECK_EQ((lsb11<T, 0>::value), T(0)); + CHECK_EQ((lsb11<T, 1>::value), T(0)); + CHECK_EQ((lsb11<T, 2>::value), T(1)); + CHECK_EQ((lsb11<T, 3>::value), T(0)); + CHECK_EQ((lsb11<T, 4>::value), T(2)); + CHECK_EQ((lsb11<T, 5>::value), T(0)); + CHECK_EQ((lsb11<T, 6>::value), T(1)); + CHECK_EQ((lsb11<T, 7>::value), T(0)); + CHECK_EQ((lsb11<T, 8>::value), T(3)); + CHECK_EQ((lsb11<T, 9>::value), T(0)); + CHECK_EQ((lsb11<T,10>::value), T(1)); + CHECK_EQ((lsb11<T,11>::value), T(0)); + CHECK_EQ((lsb11<T,12>::value), T(2)); + CHECK_EQ((lsb11<T,13>::value), T(0)); + CHECK_EQ((lsb11<T,14>::value), T(1)); + CHECK_EQ((lsb11<T,15>::value), T(0)); + CHECK_EQ((lsb11<T,16>::value), T(4)); +} + + +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("ipow.float", T, float, double) +{ + SUBCASE("base 1, signed exponent") + { + CHECK_FLOAT_EQ(ipow(T(1), int(0)), T(1)); + CHECK_FLOAT_EQ(ipow(T(1), int(1)), T(1)); + CHECK_FLOAT_EQ(ipow(T(1), int(2)), T(1)); + CHECK_FLOAT_EQ(ipow(T(1), -int(1)), T(1)); + CHECK_FLOAT_EQ(ipow(T(1), -int(2)), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 1>(int(0))), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 1>(int(1))), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 1>(int(2))), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 1>(-int(1))), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 1>(-int(2))), T(1)); + } + SUBCASE("base 1, unsigned exponent") + { + CHECK_FLOAT_EQ(ipow(T(1), unsigned(0)), T(1)); + CHECK_FLOAT_EQ(ipow(T(1), unsigned(1)), T(1)); + CHECK_FLOAT_EQ(ipow(T(1), unsigned(2)), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 1>(unsigned(0))), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 1>(unsigned(1))), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 1>(unsigned(2))), T(1)); + } + SUBCASE("base 2, signed exponent") + { + CHECK_FLOAT_EQ(ipow(T(2), int(0)), T(1)); + CHECK_FLOAT_EQ(ipow(T(2), int(1)), T(2)); + CHECK_FLOAT_EQ(ipow(T(2), int(2)), T(4)); + CHECK_FLOAT_EQ(ipow(T(2), int(7)), T(128)); + CHECK_FLOAT_EQ(ipow(T(2), -int(1)), T(0.5)); + CHECK_FLOAT_EQ(ipow(T(2), -int(2)), T(0.25)); + CHECK_FLOAT_EQ(ipow(T(2), -int(3)), T(0.125)); + CHECK_FLOAT_EQ(ipow(T(2), -int(4)), T(0.0625)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(int(0))), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(int(1))), T(2)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(int(2))), T(4)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(int(7))), T(128)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(-int(1))), T(0.5)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(-int(2))), T(0.25)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(-int(3))), T(0.125)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(-int(4))), T(0.0625)); + } + SUBCASE("base 2, unsigned exponent") + { + CHECK_FLOAT_EQ(ipow(T(2), unsigned(0)), T(1)); + CHECK_FLOAT_EQ(ipow(T(2), unsigned(1)), T(2)); + CHECK_FLOAT_EQ(ipow(T(2), unsigned(2)), T(4)); + CHECK_FLOAT_EQ(ipow(T(2), unsigned(6)), T(64)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(unsigned(0))), T(1)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(unsigned(1))), T(2)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(unsigned(2))), T(4)); + CHECK_FLOAT_EQ((ipow<T, int, 2>(unsigned(6))), T(64)); + } +} + +TEST_CASE_TEMPLATE("ipow", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + SUBCASE("base 1, signed exponent") + { + CHECK_EQ(ipow(T(1), int(0)), T(1)); + CHECK_EQ(ipow(T(1), int(1)), T(1)); + CHECK_EQ(ipow(T(1), int(2)), T(1)); + CHECK_EQ(ipow(T(1), -int(1)), T(1)); + CHECK_EQ(ipow(T(1), -int(2)), T(1)); + CHECK_EQ(ipow<T, T(1)>(int(0)), T(1)); + CHECK_EQ(ipow<T, T(1)>(int(1)), T(1)); + CHECK_EQ(ipow<T, T(1)>(int(2)), T(1)); + CHECK_EQ(ipow<T, T(1)>(-int(1)), T(1)); + CHECK_EQ(ipow<T, T(1)>(-int(2)), T(1)); + } + SUBCASE("base 1, unsigned exponent") + { + CHECK_EQ(ipow(T(1), unsigned(0)), T(1)); + CHECK_EQ(ipow(T(1), unsigned(1)), T(1)); + CHECK_EQ(ipow(T(1), unsigned(2)), T(1)); + CHECK_EQ(ipow<T, T(1)>(unsigned(0)), T(1)); + CHECK_EQ(ipow<T, T(1)>(unsigned(1)), T(1)); + CHECK_EQ(ipow<T, T(1)>(unsigned(2)), T(1)); + } + SUBCASE("base 2, signed exponent") + { + CHECK_EQ(ipow(T(2), int(0)), T(1)); + CHECK_EQ(ipow(T(2), int(1)), T(2)); + CHECK_EQ(ipow(T(2), int(2)), T(4)); + CHECK_EQ(ipow(T(2), int(6)), T(64)); + CHECK_EQ(ipow(T(2), -int(1)), T(0)); + CHECK_EQ(ipow(T(2), -int(2)), T(0)); + CHECK_EQ(ipow(T(2), -int(6)), T(0)); + CHECK_EQ(ipow<T, T(2)>(int(0)), T(1)); + CHECK_EQ(ipow<T, T(2)>(int(1)), T(2)); + CHECK_EQ(ipow<T, T(2)>(int(2)), T(4)); + CHECK_EQ(ipow<T, T(2)>(int(6)), T(64)); + CHECK_EQ(ipow<T, T(2)>(-int(1)), T(0)); + CHECK_EQ(ipow<T, T(2)>(-int(2)), T(0)); + CHECK_EQ(ipow<T, T(2)>(-int(7)), T(0)); + } + SUBCASE("base 2, unsigned exponent") + { + CHECK_EQ(ipow(T(2), unsigned(0)), T(1)); + CHECK_EQ(ipow(T(2), unsigned(1)), T(2)); + CHECK_EQ(ipow(T(2), unsigned(2)), T(4)); + CHECK_EQ(ipow(T(2), unsigned(6)), T(64)); + CHECK_EQ(ipow<T, T(2)>(unsigned(0)), T(1)); + CHECK_EQ(ipow<T, T(2)>(unsigned(1)), T(2)); + CHECK_EQ(ipow<T, T(2)>(unsigned(2)), T(4)); + CHECK_EQ(ipow<T, T(2)>(unsigned(6)), T(64)); + } +} + + +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("msb.basic", T, uint8_t, uint16_t, uint32_t, uint64_t) +{ + CHECK_EQ(msb(T( 1)), 0u); + CHECK_EQ(msb(T( 2)), 1u); + CHECK_EQ(msb(T( 3)), 1u); + CHECK_EQ(msb(T( 4)), 2u); + CHECK_EQ(msb(T( 5)), 2u); + CHECK_EQ(msb(T( 6)), 2u); + CHECK_EQ(msb(T( 7)), 2u); + CHECK_EQ(msb(T( 8)), 3u); + CHECK_EQ(msb(T( 9)), 3u); + CHECK_EQ(msb(T(10)), 3u); + CHECK_EQ(msb(T(11)), 3u); + CHECK_EQ(msb(T(12)), 3u); + CHECK_EQ(msb(T(13)), 3u); + CHECK_EQ(msb(T(14)), 3u); + CHECK_EQ(msb(T(15)), 3u); + CHECK_EQ(msb(T(16)), 4u); + CHECK_EQ(msb(std::numeric_limits<T>::max()), 8u * sizeof(T) - 1u); +} + +TEST_CASE_TEMPLATE("msb11.basic", T, uint8_t, uint16_t, uint32_t, uint64_t) +{ + CHECK_EQ((msb11<T,T( 1)>::value), T(0)); + CHECK_EQ((msb11<T,T( 2)>::value), T(1)); + CHECK_EQ((msb11<T,T( 3)>::value), T(1)); + CHECK_EQ((msb11<T,T( 4)>::value), T(2)); + CHECK_EQ((msb11<T,T( 5)>::value), T(2)); + CHECK_EQ((msb11<T,T( 6)>::value), T(2)); + CHECK_EQ((msb11<T,T( 7)>::value), T(2)); + CHECK_EQ((msb11<T,T( 8)>::value), T(3)); + CHECK_EQ((msb11<T,T( 9)>::value), T(3)); + CHECK_EQ((msb11<T,T(10)>::value), T(3)); + CHECK_EQ((msb11<T,T(11)>::value), T(3)); + CHECK_EQ((msb11<T,T(12)>::value), T(3)); + CHECK_EQ((msb11<T,T(13)>::value), T(3)); + CHECK_EQ((msb11<T,T(14)>::value), T(3)); + CHECK_EQ((msb11<T,T(15)>::value), T(3)); + CHECK_EQ((msb11<T,T(16)>::value), T(4)); + CHECK_EQ((msb11<T,std::numeric_limits<T>::max()>::value), 8u * sizeof(T) - 1u); +} + + +//----------------------------------------------------------------------------- +// contiguous mask + +template<class T> T _mask() { return T(0); } +template<class T, class... Bits> T _mask(int bit, Bits ...bits) { return (T)(T(1) << bit | _mask<T>(bits...)); } + +TEST_CASE_TEMPLATE("contiguous_mask.basic", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + CHECK_EQ(contiguous_mask(0, 0), _mask<T>()); + CHECK_EQ(contiguous_mask(0, 1), _mask<T>(0)); + CHECK_EQ(contiguous_mask(0, 2), _mask<T>(0, 1)); + CHECK_EQ(contiguous_mask(0, 3), _mask<T>(0, 1, 2)); + CHECK_EQ(contiguous_mask(0, 4), _mask<T>(0, 1, 2, 3)); + CHECK_EQ(contiguous_mask(1, 4), _mask<T>( 1, 2, 3)); + CHECK_EQ(contiguous_mask(2, 4), _mask<T>( 2, 3)); + CHECK_EQ(contiguous_mask(3, 4), _mask<T>( 3)); + CHECK_EQ(contiguous_mask(4, 4), _mask<T>()); +} + +TEST_CASE_TEMPLATE("contiguous_mask11.basic", T, int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t) +{ + CHECK_EQ((contiguous_mask11<T, 0, 0>::value), _mask<T>()); + CHECK_EQ((contiguous_mask11<T, 0, 1>::value), _mask<T>(0)); + CHECK_EQ((contiguous_mask11<T, 0, 2>::value), _mask<T>(0, 1)); + CHECK_EQ((contiguous_mask11<T, 0, 3>::value), _mask<T>(0, 1, 2)); + CHECK_EQ((contiguous_mask11<T, 0, 4>::value), _mask<T>(0, 1, 2, 3)); + CHECK_EQ((contiguous_mask11<T, 1, 4>::value), _mask<T>( 1, 2, 3)); + CHECK_EQ((contiguous_mask11<T, 2, 4>::value), _mask<T>( 2, 3)); + CHECK_EQ((contiguous_mask11<T, 3, 4>::value), _mask<T>( 3)); + CHECK_EQ((contiguous_mask11<T, 4, 4>::value), _mask<T>()); +} + + +//----------------------------------------------------------------------------- + + +template<size_t N> struct sz { char buf[N]; }; +template< > struct sz<0> { }; +template<size_t F, size_t S> void check_tp() +{ + #if defined(__clang__) + # pragma clang diagnostic push + #elif defined(__GNUC__) + # pragma GCC diagnostic push + # if __GNUC__ >= 7 + # pragma GCC diagnostic ignored "-Wduplicated-branches" + # endif + #endif + size_t expected; + if(F != 0 && S != 0) expected = F+S; + else if(F == 0 && S != 0) expected = S; + else if(F != 0 && S == 0) expected = F; // -Wduplicated-branches: false positive here + else /* F == 0 && S == 0)*/expected = 1; + #if defined(__clang__) + # pragma clang diagnostic pop + #elif defined(__GNUC__) + # pragma GCC diagnostic pop + #endif + INFO("F=" << F << " S=" << S); + CHECK_EQ(sizeof(tight_pair<sz<F>, sz<S>>), expected); +} + + +TEST_CASE("tight_pair.basic") +{ + check_tp<0,0>(); + check_tp<0,1>(); + check_tp<0,2>(); + check_tp<0,3>(); + check_tp<0,4>(); + + check_tp<0,0>(); + check_tp<1,0>(); + check_tp<2,0>(); + check_tp<3,0>(); + check_tp<4,0>(); + + check_tp<0,0>(); + check_tp<1,1>(); + check_tp<2,2>(); + check_tp<3,3>(); + check_tp<4,4>(); +} + +} // namespace c4 + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_numbers.hpp b/thirdparty/ryml/ext/c4core/test/test_numbers.hpp new file mode 100644 index 000000000..d310b36ba --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_numbers.hpp @@ -0,0 +1,1863 @@ +#ifndef C4CORE_SINGLE_HEADER +#include <stdint.h> +#include <c4/substr.hpp> +#endif +#include <iostream> + +namespace c4 { + +template<class T> +struct overflow64case +{ + bool is_overflow; + T wrapped; + csubstr dec; + csubstr hex; + csubstr oct; + csubstr bin; +}; + +template<class T> +struct overflow64cases; + +#define oc(T, is_overflow, wrapped, dec, hex, oct, bin) \ + overflow64case<T>{is_overflow, wrapped, csubstr{dec}, csubstr{hex}, csubstr{oct}, csubstr{bin}} + +template<> +struct overflow64cases<int64_t> +{ + #define INT64_1(v) INT64_C(v) - INT64_C(1) // the min value is not representable! + static constexpr const overflow64case<int64_t> values[] = { + oc(int64_t, true , INT64_C( 9223372036854775803), "-9223372036854775813", "-0x8000000000000005", "-0o1000000000000000000005", "-0b1000000000000000000000000000000000000000000000000000000000000101"), + oc(int64_t, true , INT64_C( 9223372036854775804), "-9223372036854775812", "-0x8000000000000004", "-0o1000000000000000000004", "-0b1000000000000000000000000000000000000000000000000000000000000100"), + oc(int64_t, true , INT64_C( 9223372036854775805), "-9223372036854775811", "-0x8000000000000003", "-0o1000000000000000000003", "-0b1000000000000000000000000000000000000000000000000000000000000011"), + oc(int64_t, true , INT64_C( 9223372036854775806), "-9223372036854775810", "-0x8000000000000002", "-0o1000000000000000000002", "-0b1000000000000000000000000000000000000000000000000000000000000010"), + oc(int64_t, true , INT64_C( 9223372036854775807), "-9223372036854775809", "-0x8000000000000001", "-0o1000000000000000000001", "-0b1000000000000000000000000000000000000000000000000000000000000001"), + oc(int64_t, false, INT64_1(-9223372036854775807), "-9223372036854775808", "-0x8000000000000000", "-0o1000000000000000000000", "-0b1000000000000000000000000000000000000000000000000000000000000000"), + oc(int64_t, false, INT64_C(-9223372036854775807), "-9223372036854775807", "-0x7fffffffffffffff", "-0o777777777777777777777" , "-0b111111111111111111111111111111111111111111111111111111111111111"), + oc(int64_t, false, INT64_C(-9223372036854775806), "-9223372036854775806", "-0x7ffffffffffffffe", "-0o777777777777777777776" , "-0b111111111111111111111111111111111111111111111111111111111111110"), + oc(int64_t, false, INT64_C(-9223372036854775805), "-9223372036854775805", "-0x7ffffffffffffffd", "-0o777777777777777777775" , "-0b111111111111111111111111111111111111111111111111111111111111101"), + oc(int64_t, false, INT64_C( 9223372036854775804), "9223372036854775804", "0x7ffffffffffffffc", "0o777777777777777777774" , "0b111111111111111111111111111111111111111111111111111111111111100"), + oc(int64_t, false, INT64_C( 9223372036854775805), "9223372036854775805", "0x7ffffffffffffffd", "0o777777777777777777775" , "0b111111111111111111111111111111111111111111111111111111111111101"), + oc(int64_t, false, INT64_C( 9223372036854775806), "9223372036854775806", "0x7ffffffffffffffe", "0o777777777777777777776" , "0b111111111111111111111111111111111111111111111111111111111111110"), + oc(int64_t, false, INT64_C( 9223372036854775807), "9223372036854775807", "0x7fffffffffffffff", "0o777777777777777777777" , "0b111111111111111111111111111111111111111111111111111111111111111"), + oc(int64_t, true , INT64_1(-9223372036854775807), "9223372036854775808", "0x8000000000000000", "0o1000000000000000000000", "0b1000000000000000000000000000000000000000000000000000000000000000"), + oc(int64_t, true , INT64_C(-9223372036854775807), "9223372036854775809", "0x8000000000000001", "0o1000000000000000000001", "0b1000000000000000000000000000000000000000000000000000000000000001"), + oc(int64_t, true , INT64_C(-9223372036854775806), "9223372036854775810", "0x8000000000000002", "0o1000000000000000000002", "0b1000000000000000000000000000000000000000000000000000000000000010"), + oc(int64_t, true , INT64_C(-9223372036854775805), "9223372036854775811", "0x8000000000000003", "0o1000000000000000000003", "0b1000000000000000000000000000000000000000000000000000000000000011"), + oc(int64_t, true , INT64_C(-9223372036854775804), "9223372036854775812", "0x8000000000000004", "0o1000000000000000000004", "0b1000000000000000000000000000000000000000000000000000000000000100"), + }; +}; + +template<> +struct overflow64cases<uint64_t> +{ + static constexpr const overflow64case<uint64_t> values[] = { + oc(uint64_t, true , UINT64_C(18446744073709551611), "-5" , "-0x5" , "-0o5" , "-0b101"), + oc(uint64_t, true , UINT64_C(18446744073709551612), "-4" , "-0x4" , "-0o4" , "-0b100"), + oc(uint64_t, true , UINT64_C(18446744073709551613), "-3" , "-0x3" , "-0o3" , "-0b11"), + oc(uint64_t, true , UINT64_C(18446744073709551614), "-2" , "-0x2" , "-0o2" , "-0b10"), + oc(uint64_t, true , UINT64_C(18446744073709551615), "-1" , "-0x1" , "-0o1" , "-0b1"), + oc(uint64_t, false, UINT64_C( 0), "0" , "0x0" , "0o0" , "0b0"), + oc(uint64_t, false, UINT64_C( 1), "1" , "0x1" , "0o1" , "0b1"), + oc(uint64_t, false, UINT64_C( 2), "2" , "0x2" , "0o2" , "0b10"), + oc(uint64_t, false, UINT64_C( 3), "3" , "0x3" , "0o3" , "0b11"), + oc(uint64_t, false, UINT64_C(18446744073709551612), "18446744073709551612", "0xfffffffffffffffc" , "0o1777777777777777777774" , "0b1111111111111111111111111111111111111111111111111111111111111100"), + oc(uint64_t, false, UINT64_C(18446744073709551613), "18446744073709551613", "0xfffffffffffffffd" , "0o1777777777777777777775" , "0b1111111111111111111111111111111111111111111111111111111111111101"), + oc(uint64_t, false, UINT64_C(18446744073709551614), "18446744073709551614", "0xfffffffffffffffe" , "0o1777777777777777777776" , "0b1111111111111111111111111111111111111111111111111111111111111110"), + oc(uint64_t, false, UINT64_C(18446744073709551615), "18446744073709551615", "0xffffffffffffffff" , "0o1777777777777777777777" , "0b1111111111111111111111111111111111111111111111111111111111111111"), + oc(uint64_t, true , UINT64_C( 0), "18446744073709551616", "0x10000000000000000", "0o20000000000000000000000", "0b10000000000000000000000000000000000000000000000000000000000000000"), + oc(uint64_t, true , UINT64_C( 1), "18446744073709551617", "0x10000000000000001", "0o20000000000000000000001", "0b10000000000000000000000000000000000000000000000000000000000000001"), + oc(uint64_t, true , UINT64_C( 2), "18446744073709551618", "0x10000000000000002", "0o20000000000000000000002", "0b10000000000000000000000000000000000000000000000000000000000000010"), + oc(uint64_t, true , UINT64_C( 3), "18446744073709551619", "0x10000000000000003", "0o20000000000000000000003", "0b10000000000000000000000000000000000000000000000000000000000000011"), + oc(uint64_t, true , UINT64_C( 4), "18446744073709551620", "0x10000000000000004", "0o20000000000000000000004", "0b10000000000000000000000000000000000000000000000000000000000000100"), + oc(uint64_t, true , UINT64_C( 5), "18446744073709551621", "0x10000000000000005", "0o20000000000000000000005", "0b10000000000000000000000000000000000000000000000000000000000000101"), + }; +}; + +constexpr const overflow64case<int64_t> overflow64cases<int64_t>::values[]; +constexpr const overflow64case<uint64_t> overflow64cases<uint64_t>::values[]; + +#undef oc + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +struct invalid_case +{ + csubstr dec, hex, oct, bin; +}; + +C4_INLINE_CONSTEXPR const invalid_case invalid_cases[] = { +#define ic(dec, hex, oct, bin) \ + invalid_case{csubstr{dec}, csubstr{hex}, csubstr{oct}, csubstr{bin}} + ic("" , "" , "" , ""), + ic(" " , " " , " " , " "), + ic("." , "." , "." , "."), + ic("-" , "-" , "-" , "-"), + ic("\t" , "\t" , "\t" , "\t"), + ic("\n" , "\n" , "\n" , "\n"), + ic("...", "..." , "..." , "..."), + ic("===", "===" , "===" , "==="), + ic("=??", "???" , "???" , "???"), + ic("12a", "0x12g", "0o128", "0b102"), + ic("0.1", "0x1.2", "0o1.2", "0b1.1"), + ic("0,1", "0x1,2", "0o1,2", "0b1,1"), + ic("zz" , "0xzz" , "0ozz" , "0bzz"), + ic("" , "0x" , "0o" , "0b"), + ic("- " , "-0x" , "-0o" , "-0b"), + ic("2 0", "0x2 0", "0o2 0", "0b1 0"), + ic(" 2" , " 0x2" , " 0o2" , " 0b1"), + ic("\t2", "\t0x2", "\t0o2", "\t0b1"), + ic("\n2", "nt0x2", "\n0o2", "\n0b1"), + ic("2 " , "0x2 " , "0o2 " , "0b1 "), + ic("2\t", "0x2\t", "0o2\t", "0b1\t"), + ic("2\n", "0x2\n", "0o2\n", "0b1\n"), + ic("nan", "nan", "nan", "nan"), + ic("NaN", "NaN", "NaN", "NaN"), + ic("Inf", "Inf", "Inf", "Inf"), + ic("inf", "inf", "inf", "inf"), + ic("infinity", "infinity", "infinity", "infinity"), + ic("Infinity", "Infinity", "Infinity", "Infinity"), + ic("somevalue", "somevalue", "somevalue", "somevalue"), + ic("2345kjhiuy3245", "2345kjhiuy3245", "2345kjhiuy3245", "2345kjhiuy3245"), +#undef ic +}; + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +template<class T> +struct number_case +{ + T val; + csubstr dec; + csubstr hex; + csubstr oct; + csubstr bin; +}; + +template<class T> +std::ostream& operator<< (std::ostream& s, number_case<T> const& nc) +{ + using upcast_t = typename std::conditional<sizeof(T)!=1, T, + typename std::conditional<std::is_signed<T>::value, + int64_t, + uint64_t>::type>::type; + s << "val=" << (upcast_t)nc.val << " " + << "dec=" << nc.dec << " " + << "hex=" << nc.hex << " " + << "oct=" << nc.oct << " " + << "bin=" << nc.bin; + return s; +} + +template<class T> struct numbers; + +#define ITER_NUMBERS(ty, number_name) for(number_case<ty> const& C4_RESTRICT number_name : numbers<ty>::vals) + +C4_SUPPRESS_WARNING_MSVC_PUSH +C4_SUPPRESS_WARNING_MSVC(4146) +C4_SUPPRESS_WARNING_MSVC(4305) +C4_SUPPRESS_WARNING_MSVC(4310) + +// these numbers were generated with printintegers.py, in this dir. + +template<> +struct numbers<int8_t> +{ + using value_type = int8_t; + static C4_INLINE_CONSTEXPR const number_case<int8_t> vals[] = { +#define nc(val, dec, hex, bin, oct) \ + number_case<value_type>{(value_type)INT8_C(val), csubstr{dec}, csubstr{hex}, csubstr{bin}, csubstr{oct}} + nc(-128, "-128", "-0x80", "-0o200", "-0b10000000"), + nc(-127, "-127", "-0x7f", "-0o177", "-0b1111111"), + nc(-126, "-126", "-0x7e", "-0o176", "-0b1111110"), + nc(-125, "-125", "-0x7d", "-0o175", "-0b1111101"), + nc(-124, "-124", "-0x7c", "-0o174", "-0b1111100"), + nc(-123, "-123", "-0x7b", "-0o173", "-0b1111011"), + nc(-101, "-101", "-0x65", "-0o145", "-0b1100101"), + nc(-100, "-100", "-0x64", "-0o144", "-0b1100100"), + nc(-99, "-99", "-0x63", "-0o143", "-0b1100011"), + nc(-65, "-65", "-0x41", "-0o101", "-0b1000001"), + nc(-64, "-64", "-0x40", "-0o100", "-0b1000000"), + nc(-63, "-63", "-0x3f", "-0o77", "-0b111111"), + nc(-33, "-33", "-0x21", "-0o41", "-0b100001"), + nc(-32, "-32", "-0x20", "-0o40", "-0b100000"), + nc(-31, "-31", "-0x1f", "-0o37", "-0b11111"), + nc(-17, "-17", "-0x11", "-0o21", "-0b10001"), + nc(-16, "-16", "-0x10", "-0o20", "-0b10000"), + nc(-15, "-15", "-0xf", "-0o17", "-0b1111"), + nc(-12, "-12", "-0xc", "-0o14", "-0b1100"), + nc(-11, "-11", "-0xb", "-0o13", "-0b1011"), + nc(-10, "-10", "-0xa", "-0o12", "-0b1010"), + nc(-9, "-9", "-0x9", "-0o11", "-0b1001"), + nc(-8, "-8", "-0x8", "-0o10", "-0b1000"), + nc(-7, "-7", "-0x7", "-0o7", "-0b111"), + nc(-6, "-6", "-0x6", "-0o6", "-0b110"), + nc(-5, "-5", "-0x5", "-0o5", "-0b101"), + nc(-4, "-4", "-0x4", "-0o4", "-0b100"), + nc(-3, "-3", "-0x3", "-0o3", "-0b11"), + nc(-2, "-2", "-0x2", "-0o2", "-0b10"), + nc(-1, "-1", "-0x1", "-0o1", "-0b1"), + nc(0, "0", "0x0", "0o0", "0b0"), + nc(1, "1", "0x1", "0o1", "0b1"), + nc(2, "2", "0x2", "0o2", "0b10"), + nc(3, "3", "0x3", "0o3", "0b11"), + nc(4, "4", "0x4", "0o4", "0b100"), + nc(5, "5", "0x5", "0o5", "0b101"), + nc(6, "6", "0x6", "0o6", "0b110"), + nc(7, "7", "0x7", "0o7", "0b111"), + nc(8, "8", "0x8", "0o10", "0b1000"), + nc(9, "9", "0x9", "0o11", "0b1001"), + nc(10, "10", "0xa", "0o12", "0b1010"), + nc(11, "11", "0xb", "0o13", "0b1011"), + nc(12, "12", "0xc", "0o14", "0b1100"), + nc(13, "13", "0xd", "0o15", "0b1101"), + nc(15, "15", "0xf", "0o17", "0b1111"), + nc(16, "16", "0x10", "0o20", "0b10000"), + nc(17, "17", "0x11", "0o21", "0b10001"), + nc(31, "31", "0x1f", "0o37", "0b11111"), + nc(32, "32", "0x20", "0o40", "0b100000"), + nc(33, "33", "0x21", "0o41", "0b100001"), + nc(63, "63", "0x3f", "0o77", "0b111111"), + nc(64, "64", "0x40", "0o100", "0b1000000"), + nc(65, "65", "0x41", "0o101", "0b1000001"), + nc(99, "99", "0x63", "0o143", "0b1100011"), + nc(100, "100", "0x64", "0o144", "0b1100100"), + nc(101, "101", "0x65", "0o145", "0b1100101"), + nc(122, "122", "0x7a", "0o172", "0b1111010"), + nc(123, "123", "0x7b", "0o173", "0b1111011"), + nc(124, "124", "0x7c", "0o174", "0b1111100"), + nc(125, "125", "0x7d", "0o175", "0b1111101"), + nc(126, "126", "0x7e", "0o176", "0b1111110"), + nc(127, "127", "0x7f", "0o177", "0b1111111"), +#undef nc + }; +}; + + +template<> +struct numbers<uint8_t> +{ + using value_type = uint8_t; + static C4_INLINE_CONSTEXPR const number_case<uint8_t> vals[] = { +#define nc(val, dec, hex, bin, oct) \ + number_case<value_type>{(value_type)UINT8_C(val), csubstr{dec}, csubstr{hex}, csubstr{bin}, csubstr{oct}} + nc(0, "0", "0x0", "0o0", "0b0"), + nc(1, "1", "0x1", "0o1", "0b1"), + nc(2, "2", "0x2", "0o2", "0b10"), + nc(3, "3", "0x3", "0o3", "0b11"), + nc(4, "4", "0x4", "0o4", "0b100"), + nc(5, "5", "0x5", "0o5", "0b101"), + nc(6, "6", "0x6", "0o6", "0b110"), + nc(7, "7", "0x7", "0o7", "0b111"), + nc(8, "8", "0x8", "0o10", "0b1000"), + nc(9, "9", "0x9", "0o11", "0b1001"), + nc(10, "10", "0xa", "0o12", "0b1010"), + nc(11, "11", "0xb", "0o13", "0b1011"), + nc(15, "15", "0xf", "0o17", "0b1111"), + nc(16, "16", "0x10", "0o20", "0b10000"), + nc(17, "17", "0x11", "0o21", "0b10001"), + nc(24, "24", "0x18", "0o30", "0b11000"), + nc(25, "25", "0x19", "0o31", "0b11001"), + nc(26, "26", "0x1a", "0o32", "0b11010"), + nc(31, "31", "0x1f", "0o37", "0b11111"), + nc(32, "32", "0x20", "0o40", "0b100000"), + nc(33, "33", "0x21", "0o41", "0b100001"), + nc(63, "63", "0x3f", "0o77", "0b111111"), + nc(64, "64", "0x40", "0o100", "0b1000000"), + nc(65, "65", "0x41", "0o101", "0b1000001"), + nc(99, "99", "0x63", "0o143", "0b1100011"), + nc(100, "100", "0x64", "0o144", "0b1100100"), + nc(101, "101", "0x65", "0o145", "0b1100101"), + nc(127, "127", "0x7f", "0o177", "0b1111111"), + nc(128, "128", "0x80", "0o200", "0b10000000"), + nc(129, "129", "0x81", "0o201", "0b10000001"), + nc(251, "251", "0xfb", "0o373", "0b11111011"), + nc(252, "252", "0xfc", "0o374", "0b11111100"), + nc(253, "253", "0xfd", "0o375", "0b11111101"), + nc(254, "254", "0xfe", "0o376", "0b11111110"), + nc(255, "255", "0xff", "0o377", "0b11111111"), +#undef nc + }; +}; + + +template<> +struct numbers<int16_t> +{ + using value_type = int16_t; + static C4_INLINE_CONSTEXPR const number_case<int16_t> vals[] = { +#define nc(val, dec, hex, bin, oct) \ + number_case<value_type>{(value_type)INT16_C(val), csubstr{dec}, csubstr{hex}, csubstr{bin}, csubstr{oct}} + nc(-32768, "-32768", "-0x8000", "-0o100000", "-0b1000000000000000"), + nc(-32767, "-32767", "-0x7fff", "-0o77777", "-0b111111111111111"), + nc(-32766, "-32766", "-0x7ffe", "-0o77776", "-0b111111111111110"), + nc(-32765, "-32765", "-0x7ffd", "-0o77775", "-0b111111111111101"), + nc(-32764, "-32764", "-0x7ffc", "-0o77774", "-0b111111111111100"), + nc(-32763, "-32763", "-0x7ffb", "-0o77773", "-0b111111111111011"), + nc(-16385, "-16385", "-0x4001", "-0o40001", "-0b100000000000001"), + nc(-16384, "-16384", "-0x4000", "-0o40000", "-0b100000000000000"), + nc(-16383, "-16383", "-0x3fff", "-0o37777", "-0b11111111111111"), + nc(-10001, "-10001", "-0x2711", "-0o23421", "-0b10011100010001"), + nc(-10000, "-10000", "-0x2710", "-0o23420", "-0b10011100010000"), + nc(-9999, "-9999", "-0x270f", "-0o23417", "-0b10011100001111"), + nc(-8193, "-8193", "-0x2001", "-0o20001", "-0b10000000000001"), + nc(-8192, "-8192", "-0x2000", "-0o20000", "-0b10000000000000"), + nc(-8191, "-8191", "-0x1fff", "-0o17777", "-0b1111111111111"), + nc(-4097, "-4097", "-0x1001", "-0o10001", "-0b1000000000001"), + nc(-4096, "-4096", "-0x1000", "-0o10000", "-0b1000000000000"), + nc(-4095, "-4095", "-0xfff", "-0o7777", "-0b111111111111"), + nc(-3276, "-3276", "-0xccc", "-0o6314", "-0b110011001100"), + nc(-2049, "-2049", "-0x801", "-0o4001", "-0b100000000001"), + nc(-2048, "-2048", "-0x800", "-0o4000", "-0b100000000000"), + nc(-2047, "-2047", "-0x7ff", "-0o3777", "-0b11111111111"), + nc(-1025, "-1025", "-0x401", "-0o2001", "-0b10000000001"), + nc(-1024, "-1024", "-0x400", "-0o2000", "-0b10000000000"), + nc(-1023, "-1023", "-0x3ff", "-0o1777", "-0b1111111111"), + nc(-1001, "-1001", "-0x3e9", "-0o1751", "-0b1111101001"), + nc(-1000, "-1000", "-0x3e8", "-0o1750", "-0b1111101000"), + nc(-999, "-999", "-0x3e7", "-0o1747", "-0b1111100111"), + nc(-513, "-513", "-0x201", "-0o1001", "-0b1000000001"), + nc(-512, "-512", "-0x200", "-0o1000", "-0b1000000000"), + nc(-511, "-511", "-0x1ff", "-0o777", "-0b111111111"), + nc(-327, "-327", "-0x147", "-0o507", "-0b101000111"), + nc(-257, "-257", "-0x101", "-0o401", "-0b100000001"), + nc(-256, "-256", "-0x100", "-0o400", "-0b100000000"), + nc(-255, "-255", "-0xff", "-0o377", "-0b11111111"), + nc(-129, "-129", "-0x81", "-0o201", "-0b10000001"), + nc(-128, "-128", "-0x80", "-0o200", "-0b10000000"), + nc(-127, "-127", "-0x7f", "-0o177", "-0b1111111"), + nc(-101, "-101", "-0x65", "-0o145", "-0b1100101"), + nc(-100, "-100", "-0x64", "-0o144", "-0b1100100"), + nc(-99, "-99", "-0x63", "-0o143", "-0b1100011"), + nc(-65, "-65", "-0x41", "-0o101", "-0b1000001"), + nc(-64, "-64", "-0x40", "-0o100", "-0b1000000"), + nc(-63, "-63", "-0x3f", "-0o77", "-0b111111"), + nc(-33, "-33", "-0x21", "-0o41", "-0b100001"), + nc(-32, "-32", "-0x20", "-0o40", "-0b100000"), + nc(-31, "-31", "-0x1f", "-0o37", "-0b11111"), + nc(-17, "-17", "-0x11", "-0o21", "-0b10001"), + nc(-16, "-16", "-0x10", "-0o20", "-0b10000"), + nc(-15, "-15", "-0xf", "-0o17", "-0b1111"), + nc(-11, "-11", "-0xb", "-0o13", "-0b1011"), + nc(-10, "-10", "-0xa", "-0o12", "-0b1010"), + nc(-9, "-9", "-0x9", "-0o11", "-0b1001"), + nc(-8, "-8", "-0x8", "-0o10", "-0b1000"), + nc(-7, "-7", "-0x7", "-0o7", "-0b111"), + nc(-6, "-6", "-0x6", "-0o6", "-0b110"), + nc(-5, "-5", "-0x5", "-0o5", "-0b101"), + nc(-4, "-4", "-0x4", "-0o4", "-0b100"), + nc(-3, "-3", "-0x3", "-0o3", "-0b11"), + nc(-2, "-2", "-0x2", "-0o2", "-0b10"), + nc(-1, "-1", "-0x1", "-0o1", "-0b1"), + nc(0, "0", "0x0", "0o0", "0b0"), + nc(1, "1", "0x1", "0o1", "0b1"), + nc(2, "2", "0x2", "0o2", "0b10"), + nc(3, "3", "0x3", "0o3", "0b11"), + nc(4, "4", "0x4", "0o4", "0b100"), + nc(5, "5", "0x5", "0o5", "0b101"), + nc(6, "6", "0x6", "0o6", "0b110"), + nc(7, "7", "0x7", "0o7", "0b111"), + nc(8, "8", "0x8", "0o10", "0b1000"), + nc(9, "9", "0x9", "0o11", "0b1001"), + nc(10, "10", "0xa", "0o12", "0b1010"), + nc(11, "11", "0xb", "0o13", "0b1011"), + nc(15, "15", "0xf", "0o17", "0b1111"), + nc(16, "16", "0x10", "0o20", "0b10000"), + nc(17, "17", "0x11", "0o21", "0b10001"), + nc(31, "31", "0x1f", "0o37", "0b11111"), + nc(32, "32", "0x20", "0o40", "0b100000"), + nc(33, "33", "0x21", "0o41", "0b100001"), + nc(63, "63", "0x3f", "0o77", "0b111111"), + nc(64, "64", "0x40", "0o100", "0b1000000"), + nc(65, "65", "0x41", "0o101", "0b1000001"), + nc(99, "99", "0x63", "0o143", "0b1100011"), + nc(100, "100", "0x64", "0o144", "0b1100100"), + nc(101, "101", "0x65", "0o145", "0b1100101"), + nc(127, "127", "0x7f", "0o177", "0b1111111"), + nc(128, "128", "0x80", "0o200", "0b10000000"), + nc(129, "129", "0x81", "0o201", "0b10000001"), + nc(255, "255", "0xff", "0o377", "0b11111111"), + nc(256, "256", "0x100", "0o400", "0b100000000"), + nc(257, "257", "0x101", "0o401", "0b100000001"), + nc(326, "326", "0x146", "0o506", "0b101000110"), + nc(327, "327", "0x147", "0o507", "0b101000111"), + nc(328, "328", "0x148", "0o510", "0b101001000"), + nc(511, "511", "0x1ff", "0o777", "0b111111111"), + nc(512, "512", "0x200", "0o1000", "0b1000000000"), + nc(513, "513", "0x201", "0o1001", "0b1000000001"), + nc(999, "999", "0x3e7", "0o1747", "0b1111100111"), + nc(1000, "1000", "0x3e8", "0o1750", "0b1111101000"), + nc(1001, "1001", "0x3e9", "0o1751", "0b1111101001"), + nc(1023, "1023", "0x3ff", "0o1777", "0b1111111111"), + nc(1024, "1024", "0x400", "0o2000", "0b10000000000"), + nc(1025, "1025", "0x401", "0o2001", "0b10000000001"), + nc(2047, "2047", "0x7ff", "0o3777", "0b11111111111"), + nc(2048, "2048", "0x800", "0o4000", "0b100000000000"), + nc(2049, "2049", "0x801", "0o4001", "0b100000000001"), + nc(3275, "3275", "0xccb", "0o6313", "0b110011001011"), + nc(3276, "3276", "0xccc", "0o6314", "0b110011001100"), + nc(3277, "3277", "0xccd", "0o6315", "0b110011001101"), + nc(4095, "4095", "0xfff", "0o7777", "0b111111111111"), + nc(4096, "4096", "0x1000", "0o10000", "0b1000000000000"), + nc(4097, "4097", "0x1001", "0o10001", "0b1000000000001"), + nc(8191, "8191", "0x1fff", "0o17777", "0b1111111111111"), + nc(8192, "8192", "0x2000", "0o20000", "0b10000000000000"), + nc(8193, "8193", "0x2001", "0o20001", "0b10000000000001"), + nc(9999, "9999", "0x270f", "0o23417", "0b10011100001111"), + nc(10000, "10000", "0x2710", "0o23420", "0b10011100010000"), + nc(10001, "10001", "0x2711", "0o23421", "0b10011100010001"), + nc(16383, "16383", "0x3fff", "0o37777", "0b11111111111111"), + nc(16384, "16384", "0x4000", "0o40000", "0b100000000000000"), + nc(16385, "16385", "0x4001", "0o40001", "0b100000000000001"), + nc(32762, "32762", "0x7ffa", "0o77772", "0b111111111111010"), + nc(32763, "32763", "0x7ffb", "0o77773", "0b111111111111011"), + nc(32764, "32764", "0x7ffc", "0o77774", "0b111111111111100"), + nc(32765, "32765", "0x7ffd", "0o77775", "0b111111111111101"), + nc(32766, "32766", "0x7ffe", "0o77776", "0b111111111111110"), + nc(32767, "32767", "0x7fff", "0o77777", "0b111111111111111"), +#undef nc + }; +}; + + +template<> +struct numbers<uint16_t> +{ + using value_type = uint16_t; + static C4_INLINE_CONSTEXPR const number_case<uint16_t> vals[] = { +#define nc(val, dec, hex, bin, oct) \ + number_case<value_type>{(value_type)UINT16_C(val), csubstr{dec}, csubstr{hex}, csubstr{bin}, csubstr{oct}} + nc(0, "0", "0x0", "0o0", "0b0"), + nc(1, "1", "0x1", "0o1", "0b1"), + nc(2, "2", "0x2", "0o2", "0b10"), + nc(3, "3", "0x3", "0o3", "0b11"), + nc(4, "4", "0x4", "0o4", "0b100"), + nc(5, "5", "0x5", "0o5", "0b101"), + nc(6, "6", "0x6", "0o6", "0b110"), + nc(7, "7", "0x7", "0o7", "0b111"), + nc(8, "8", "0x8", "0o10", "0b1000"), + nc(9, "9", "0x9", "0o11", "0b1001"), + nc(10, "10", "0xa", "0o12", "0b1010"), + nc(11, "11", "0xb", "0o13", "0b1011"), + nc(15, "15", "0xf", "0o17", "0b1111"), + nc(16, "16", "0x10", "0o20", "0b10000"), + nc(17, "17", "0x11", "0o21", "0b10001"), + nc(31, "31", "0x1f", "0o37", "0b11111"), + nc(32, "32", "0x20", "0o40", "0b100000"), + nc(33, "33", "0x21", "0o41", "0b100001"), + nc(63, "63", "0x3f", "0o77", "0b111111"), + nc(64, "64", "0x40", "0o100", "0b1000000"), + nc(65, "65", "0x41", "0o101", "0b1000001"), + nc(66, "66", "0x42", "0o102", "0b1000010"), + nc(99, "99", "0x63", "0o143", "0b1100011"), + nc(100, "100", "0x64", "0o144", "0b1100100"), + nc(101, "101", "0x65", "0o145", "0b1100101"), + nc(127, "127", "0x7f", "0o177", "0b1111111"), + nc(128, "128", "0x80", "0o200", "0b10000000"), + nc(129, "129", "0x81", "0o201", "0b10000001"), + nc(255, "255", "0xff", "0o377", "0b11111111"), + nc(256, "256", "0x100", "0o400", "0b100000000"), + nc(257, "257", "0x101", "0o401", "0b100000001"), + nc(511, "511", "0x1ff", "0o777", "0b111111111"), + nc(512, "512", "0x200", "0o1000", "0b1000000000"), + nc(513, "513", "0x201", "0o1001", "0b1000000001"), + nc(654, "654", "0x28e", "0o1216", "0b1010001110"), + nc(655, "655", "0x28f", "0o1217", "0b1010001111"), + nc(656, "656", "0x290", "0o1220", "0b1010010000"), + nc(999, "999", "0x3e7", "0o1747", "0b1111100111"), + nc(1000, "1000", "0x3e8", "0o1750", "0b1111101000"), + nc(1001, "1001", "0x3e9", "0o1751", "0b1111101001"), + nc(1023, "1023", "0x3ff", "0o1777", "0b1111111111"), + nc(1024, "1024", "0x400", "0o2000", "0b10000000000"), + nc(1025, "1025", "0x401", "0o2001", "0b10000000001"), + nc(2047, "2047", "0x7ff", "0o3777", "0b11111111111"), + nc(2048, "2048", "0x800", "0o4000", "0b100000000000"), + nc(2049, "2049", "0x801", "0o4001", "0b100000000001"), + nc(4095, "4095", "0xfff", "0o7777", "0b111111111111"), + nc(4096, "4096", "0x1000", "0o10000", "0b1000000000000"), + nc(4097, "4097", "0x1001", "0o10001", "0b1000000000001"), + nc(6552, "6552", "0x1998", "0o14630", "0b1100110011000"), + nc(6553, "6553", "0x1999", "0o14631", "0b1100110011001"), + nc(6554, "6554", "0x199a", "0o14632", "0b1100110011010"), + nc(8191, "8191", "0x1fff", "0o17777", "0b1111111111111"), + nc(8192, "8192", "0x2000", "0o20000", "0b10000000000000"), + nc(8193, "8193", "0x2001", "0o20001", "0b10000000000001"), + nc(9999, "9999", "0x270f", "0o23417", "0b10011100001111"), + nc(10000, "10000", "0x2710", "0o23420", "0b10011100010000"), + nc(10001, "10001", "0x2711", "0o23421", "0b10011100010001"), + nc(16383, "16383", "0x3fff", "0o37777", "0b11111111111111"), + nc(16384, "16384", "0x4000", "0o40000", "0b100000000000000"), + nc(16385, "16385", "0x4001", "0o40001", "0b100000000000001"), + nc(32767, "32767", "0x7fff", "0o77777", "0b111111111111111"), + nc(32768, "32768", "0x8000", "0o100000", "0b1000000000000000"), + nc(32769, "32769", "0x8001", "0o100001", "0b1000000000000001"), + nc(65531, "65531", "0xfffb", "0o177773", "0b1111111111111011"), + nc(65532, "65532", "0xfffc", "0o177774", "0b1111111111111100"), + nc(65533, "65533", "0xfffd", "0o177775", "0b1111111111111101"), + nc(65534, "65534", "0xfffe", "0o177776", "0b1111111111111110"), + nc(65535, "65535", "0xffff", "0o177777", "0b1111111111111111"), +#undef nc + }; +}; + + +template<> +struct numbers<int32_t> +{ + using value_type = int32_t; + static C4_INLINE_CONSTEXPR const number_case<int32_t> vals[] = { +#define nc(val, dec, hex, bin, oct) \ + number_case<value_type>{(value_type)INT32_C(val), csubstr{dec}, csubstr{hex}, csubstr{bin}, csubstr{oct}} + nc(-2147483648, "-2147483648", "-0x80000000", "-0o20000000000", "-0b10000000000000000000000000000000"), + nc(-2147483647, "-2147483647", "-0x7fffffff", "-0o17777777777", "-0b1111111111111111111111111111111"), + nc(-2147483646, "-2147483646", "-0x7ffffffe", "-0o17777777776", "-0b1111111111111111111111111111110"), + nc(-2147483645, "-2147483645", "-0x7ffffffd", "-0o17777777775", "-0b1111111111111111111111111111101"), + nc(-2147483644, "-2147483644", "-0x7ffffffc", "-0o17777777774", "-0b1111111111111111111111111111100"), + nc(-2147483643, "-2147483643", "-0x7ffffffb", "-0o17777777773", "-0b1111111111111111111111111111011"), + nc(-1073741825, "-1073741825", "-0x40000001", "-0o10000000001", "-0b1000000000000000000000000000001"), + nc(-1073741824, "-1073741824", "-0x40000000", "-0o10000000000", "-0b1000000000000000000000000000000"), + nc(-1073741823, "-1073741823", "-0x3fffffff", "-0o7777777777", "-0b111111111111111111111111111111"), + nc(-1000000001, "-1000000001", "-0x3b9aca01", "-0o7346545001", "-0b111011100110101100101000000001"), + nc(-1000000000, "-1000000000", "-0x3b9aca00", "-0o7346545000", "-0b111011100110101100101000000000"), + nc(-999999999, "-999999999", "-0x3b9ac9ff", "-0o7346544777", "-0b111011100110101100100111111111"), + nc(-536870913, "-536870913", "-0x20000001", "-0o4000000001", "-0b100000000000000000000000000001"), + nc(-536870912, "-536870912", "-0x20000000", "-0o4000000000", "-0b100000000000000000000000000000"), + nc(-536870911, "-536870911", "-0x1fffffff", "-0o3777777777", "-0b11111111111111111111111111111"), + nc(-268435457, "-268435457", "-0x10000001", "-0o2000000001", "-0b10000000000000000000000000001"), + nc(-268435456, "-268435456", "-0x10000000", "-0o2000000000", "-0b10000000000000000000000000000"), + nc(-268435455, "-268435455", "-0xfffffff", "-0o1777777777", "-0b1111111111111111111111111111"), + nc(-214748364, "-214748364", "-0xccccccc", "-0o1463146314", "-0b1100110011001100110011001100"), + nc(-134217729, "-134217729", "-0x8000001", "-0o1000000001", "-0b1000000000000000000000000001"), + nc(-134217728, "-134217728", "-0x8000000", "-0o1000000000", "-0b1000000000000000000000000000"), + nc(-134217727, "-134217727", "-0x7ffffff", "-0o777777777", "-0b111111111111111111111111111"), + nc(-100000001, "-100000001", "-0x5f5e101", "-0o575360401", "-0b101111101011110000100000001"), + nc(-100000000, "-100000000", "-0x5f5e100", "-0o575360400", "-0b101111101011110000100000000"), + nc(-99999999, "-99999999", "-0x5f5e0ff", "-0o575360377", "-0b101111101011110000011111111"), + nc(-67108865, "-67108865", "-0x4000001", "-0o400000001", "-0b100000000000000000000000001"), + nc(-67108864, "-67108864", "-0x4000000", "-0o400000000", "-0b100000000000000000000000000"), + nc(-67108863, "-67108863", "-0x3ffffff", "-0o377777777", "-0b11111111111111111111111111"), + nc(-33554433, "-33554433", "-0x2000001", "-0o200000001", "-0b10000000000000000000000001"), + nc(-33554432, "-33554432", "-0x2000000", "-0o200000000", "-0b10000000000000000000000000"), + nc(-33554431, "-33554431", "-0x1ffffff", "-0o177777777", "-0b1111111111111111111111111"), + nc(-21474836, "-21474836", "-0x147ae14", "-0o121727024", "-0b1010001111010111000010100"), + nc(-16777217, "-16777217", "-0x1000001", "-0o100000001", "-0b1000000000000000000000001"), + nc(-16777216, "-16777216", "-0x1000000", "-0o100000000", "-0b1000000000000000000000000"), + nc(-16777215, "-16777215", "-0xffffff", "-0o77777777", "-0b111111111111111111111111"), + nc(-10000001, "-10000001", "-0x989681", "-0o46113201", "-0b100110001001011010000001"), + nc(-10000000, "-10000000", "-0x989680", "-0o46113200", "-0b100110001001011010000000"), + nc(-9999999, "-9999999", "-0x98967f", "-0o46113177", "-0b100110001001011001111111"), + nc(-8388609, "-8388609", "-0x800001", "-0o40000001", "-0b100000000000000000000001"), + nc(-8388608, "-8388608", "-0x800000", "-0o40000000", "-0b100000000000000000000000"), + nc(-8388607, "-8388607", "-0x7fffff", "-0o37777777", "-0b11111111111111111111111"), + nc(-4194305, "-4194305", "-0x400001", "-0o20000001", "-0b10000000000000000000001"), + nc(-4194304, "-4194304", "-0x400000", "-0o20000000", "-0b10000000000000000000000"), + nc(-4194303, "-4194303", "-0x3fffff", "-0o17777777", "-0b1111111111111111111111"), + nc(-2147483, "-2147483", "-0x20c49b", "-0o10142233", "-0b1000001100010010011011"), + nc(-2097153, "-2097153", "-0x200001", "-0o10000001", "-0b1000000000000000000001"), + nc(-2097152, "-2097152", "-0x200000", "-0o10000000", "-0b1000000000000000000000"), + nc(-2097151, "-2097151", "-0x1fffff", "-0o7777777", "-0b111111111111111111111"), + nc(-1048577, "-1048577", "-0x100001", "-0o4000001", "-0b100000000000000000001"), + nc(-1048576, "-1048576", "-0x100000", "-0o4000000", "-0b100000000000000000000"), + nc(-1048575, "-1048575", "-0xfffff", "-0o3777777", "-0b11111111111111111111"), + nc(-1000001, "-1000001", "-0xf4241", "-0o3641101", "-0b11110100001001000001"), + nc(-1000000, "-1000000", "-0xf4240", "-0o3641100", "-0b11110100001001000000"), + nc(-999999, "-999999", "-0xf423f", "-0o3641077", "-0b11110100001000111111"), + nc(-524289, "-524289", "-0x80001", "-0o2000001", "-0b10000000000000000001"), + nc(-524288, "-524288", "-0x80000", "-0o2000000", "-0b10000000000000000000"), + nc(-524287, "-524287", "-0x7ffff", "-0o1777777", "-0b1111111111111111111"), + nc(-262145, "-262145", "-0x40001", "-0o1000001", "-0b1000000000000000001"), + nc(-262144, "-262144", "-0x40000", "-0o1000000", "-0b1000000000000000000"), + nc(-262143, "-262143", "-0x3ffff", "-0o777777", "-0b111111111111111111"), + nc(-214748, "-214748", "-0x346dc", "-0o643334", "-0b110100011011011100"), + nc(-131073, "-131073", "-0x20001", "-0o400001", "-0b100000000000000001"), + nc(-131072, "-131072", "-0x20000", "-0o400000", "-0b100000000000000000"), + nc(-131071, "-131071", "-0x1ffff", "-0o377777", "-0b11111111111111111"), + nc(-100001, "-100001", "-0x186a1", "-0o303241", "-0b11000011010100001"), + nc(-100000, "-100000", "-0x186a0", "-0o303240", "-0b11000011010100000"), + nc(-99999, "-99999", "-0x1869f", "-0o303237", "-0b11000011010011111"), + nc(-65537, "-65537", "-0x10001", "-0o200001", "-0b10000000000000001"), + nc(-65536, "-65536", "-0x10000", "-0o200000", "-0b10000000000000000"), + nc(-65535, "-65535", "-0xffff", "-0o177777", "-0b1111111111111111"), + nc(-32769, "-32769", "-0x8001", "-0o100001", "-0b1000000000000001"), + nc(-32768, "-32768", "-0x8000", "-0o100000", "-0b1000000000000000"), + nc(-32767, "-32767", "-0x7fff", "-0o77777", "-0b111111111111111"), + nc(-21474, "-21474", "-0x53e2", "-0o51742", "-0b101001111100010"), + nc(-16385, "-16385", "-0x4001", "-0o40001", "-0b100000000000001"), + nc(-16384, "-16384", "-0x4000", "-0o40000", "-0b100000000000000"), + nc(-16383, "-16383", "-0x3fff", "-0o37777", "-0b11111111111111"), + nc(-10001, "-10001", "-0x2711", "-0o23421", "-0b10011100010001"), + nc(-10000, "-10000", "-0x2710", "-0o23420", "-0b10011100010000"), + nc(-9999, "-9999", "-0x270f", "-0o23417", "-0b10011100001111"), + nc(-8193, "-8193", "-0x2001", "-0o20001", "-0b10000000000001"), + nc(-8192, "-8192", "-0x2000", "-0o20000", "-0b10000000000000"), + nc(-8191, "-8191", "-0x1fff", "-0o17777", "-0b1111111111111"), + nc(-4097, "-4097", "-0x1001", "-0o10001", "-0b1000000000001"), + nc(-4096, "-4096", "-0x1000", "-0o10000", "-0b1000000000000"), + nc(-4095, "-4095", "-0xfff", "-0o7777", "-0b111111111111"), + nc(-2147, "-2147", "-0x863", "-0o4143", "-0b100001100011"), + nc(-2049, "-2049", "-0x801", "-0o4001", "-0b100000000001"), + nc(-2048, "-2048", "-0x800", "-0o4000", "-0b100000000000"), + nc(-2047, "-2047", "-0x7ff", "-0o3777", "-0b11111111111"), + nc(-1025, "-1025", "-0x401", "-0o2001", "-0b10000000001"), + nc(-1024, "-1024", "-0x400", "-0o2000", "-0b10000000000"), + nc(-1023, "-1023", "-0x3ff", "-0o1777", "-0b1111111111"), + nc(-1001, "-1001", "-0x3e9", "-0o1751", "-0b1111101001"), + nc(-1000, "-1000", "-0x3e8", "-0o1750", "-0b1111101000"), + nc(-999, "-999", "-0x3e7", "-0o1747", "-0b1111100111"), + nc(-513, "-513", "-0x201", "-0o1001", "-0b1000000001"), + nc(-512, "-512", "-0x200", "-0o1000", "-0b1000000000"), + nc(-511, "-511", "-0x1ff", "-0o777", "-0b111111111"), + nc(-257, "-257", "-0x101", "-0o401", "-0b100000001"), + nc(-256, "-256", "-0x100", "-0o400", "-0b100000000"), + nc(-255, "-255", "-0xff", "-0o377", "-0b11111111"), + nc(-214, "-214", "-0xd6", "-0o326", "-0b11010110"), + nc(-129, "-129", "-0x81", "-0o201", "-0b10000001"), + nc(-128, "-128", "-0x80", "-0o200", "-0b10000000"), + nc(-127, "-127", "-0x7f", "-0o177", "-0b1111111"), + nc(-101, "-101", "-0x65", "-0o145", "-0b1100101"), + nc(-100, "-100", "-0x64", "-0o144", "-0b1100100"), + nc(-99, "-99", "-0x63", "-0o143", "-0b1100011"), + nc(-65, "-65", "-0x41", "-0o101", "-0b1000001"), + nc(-64, "-64", "-0x40", "-0o100", "-0b1000000"), + nc(-63, "-63", "-0x3f", "-0o77", "-0b111111"), + nc(-33, "-33", "-0x21", "-0o41", "-0b100001"), + nc(-32, "-32", "-0x20", "-0o40", "-0b100000"), + nc(-31, "-31", "-0x1f", "-0o37", "-0b11111"), + nc(-21, "-21", "-0x15", "-0o25", "-0b10101"), + nc(-17, "-17", "-0x11", "-0o21", "-0b10001"), + nc(-16, "-16", "-0x10", "-0o20", "-0b10000"), + nc(-15, "-15", "-0xf", "-0o17", "-0b1111"), + nc(-11, "-11", "-0xb", "-0o13", "-0b1011"), + nc(-10, "-10", "-0xa", "-0o12", "-0b1010"), + nc(-9, "-9", "-0x9", "-0o11", "-0b1001"), + nc(-8, "-8", "-0x8", "-0o10", "-0b1000"), + nc(-7, "-7", "-0x7", "-0o7", "-0b111"), + nc(-6, "-6", "-0x6", "-0o6", "-0b110"), + nc(-5, "-5", "-0x5", "-0o5", "-0b101"), + nc(-4, "-4", "-0x4", "-0o4", "-0b100"), + nc(-3, "-3", "-0x3", "-0o3", "-0b11"), + nc(-2, "-2", "-0x2", "-0o2", "-0b10"), + nc(-1, "-1", "-0x1", "-0o1", "-0b1"), + nc(0, "0", "0x0", "0o0", "0b0"), + nc(1, "1", "0x1", "0o1", "0b1"), + nc(2, "2", "0x2", "0o2", "0b10"), + nc(3, "3", "0x3", "0o3", "0b11"), + nc(4, "4", "0x4", "0o4", "0b100"), + nc(5, "5", "0x5", "0o5", "0b101"), + nc(6, "6", "0x6", "0o6", "0b110"), + nc(7, "7", "0x7", "0o7", "0b111"), + nc(8, "8", "0x8", "0o10", "0b1000"), + nc(9, "9", "0x9", "0o11", "0b1001"), + nc(10, "10", "0xa", "0o12", "0b1010"), + nc(11, "11", "0xb", "0o13", "0b1011"), + nc(15, "15", "0xf", "0o17", "0b1111"), + nc(16, "16", "0x10", "0o20", "0b10000"), + nc(17, "17", "0x11", "0o21", "0b10001"), + nc(20, "20", "0x14", "0o24", "0b10100"), + nc(21, "21", "0x15", "0o25", "0b10101"), + nc(22, "22", "0x16", "0o26", "0b10110"), + nc(31, "31", "0x1f", "0o37", "0b11111"), + nc(32, "32", "0x20", "0o40", "0b100000"), + nc(33, "33", "0x21", "0o41", "0b100001"), + nc(63, "63", "0x3f", "0o77", "0b111111"), + nc(64, "64", "0x40", "0o100", "0b1000000"), + nc(65, "65", "0x41", "0o101", "0b1000001"), + nc(99, "99", "0x63", "0o143", "0b1100011"), + nc(100, "100", "0x64", "0o144", "0b1100100"), + nc(101, "101", "0x65", "0o145", "0b1100101"), + nc(127, "127", "0x7f", "0o177", "0b1111111"), + nc(128, "128", "0x80", "0o200", "0b10000000"), + nc(129, "129", "0x81", "0o201", "0b10000001"), + nc(213, "213", "0xd5", "0o325", "0b11010101"), + nc(214, "214", "0xd6", "0o326", "0b11010110"), + nc(215, "215", "0xd7", "0o327", "0b11010111"), + nc(255, "255", "0xff", "0o377", "0b11111111"), + nc(256, "256", "0x100", "0o400", "0b100000000"), + nc(257, "257", "0x101", "0o401", "0b100000001"), + nc(511, "511", "0x1ff", "0o777", "0b111111111"), + nc(512, "512", "0x200", "0o1000", "0b1000000000"), + nc(513, "513", "0x201", "0o1001", "0b1000000001"), + nc(999, "999", "0x3e7", "0o1747", "0b1111100111"), + nc(1000, "1000", "0x3e8", "0o1750", "0b1111101000"), + nc(1001, "1001", "0x3e9", "0o1751", "0b1111101001"), + nc(1023, "1023", "0x3ff", "0o1777", "0b1111111111"), + nc(1024, "1024", "0x400", "0o2000", "0b10000000000"), + nc(1025, "1025", "0x401", "0o2001", "0b10000000001"), + nc(2047, "2047", "0x7ff", "0o3777", "0b11111111111"), + nc(2048, "2048", "0x800", "0o4000", "0b100000000000"), + nc(2049, "2049", "0x801", "0o4001", "0b100000000001"), + nc(2146, "2146", "0x862", "0o4142", "0b100001100010"), + nc(2147, "2147", "0x863", "0o4143", "0b100001100011"), + nc(2148, "2148", "0x864", "0o4144", "0b100001100100"), + nc(4095, "4095", "0xfff", "0o7777", "0b111111111111"), + nc(4096, "4096", "0x1000", "0o10000", "0b1000000000000"), + nc(4097, "4097", "0x1001", "0o10001", "0b1000000000001"), + nc(8191, "8191", "0x1fff", "0o17777", "0b1111111111111"), + nc(8192, "8192", "0x2000", "0o20000", "0b10000000000000"), + nc(8193, "8193", "0x2001", "0o20001", "0b10000000000001"), + nc(9999, "9999", "0x270f", "0o23417", "0b10011100001111"), + nc(10000, "10000", "0x2710", "0o23420", "0b10011100010000"), + nc(10001, "10001", "0x2711", "0o23421", "0b10011100010001"), + nc(16383, "16383", "0x3fff", "0o37777", "0b11111111111111"), + nc(16384, "16384", "0x4000", "0o40000", "0b100000000000000"), + nc(16385, "16385", "0x4001", "0o40001", "0b100000000000001"), + nc(21473, "21473", "0x53e1", "0o51741", "0b101001111100001"), + nc(21474, "21474", "0x53e2", "0o51742", "0b101001111100010"), + nc(21475, "21475", "0x53e3", "0o51743", "0b101001111100011"), + nc(32767, "32767", "0x7fff", "0o77777", "0b111111111111111"), + nc(32768, "32768", "0x8000", "0o100000", "0b1000000000000000"), + nc(32769, "32769", "0x8001", "0o100001", "0b1000000000000001"), + nc(65535, "65535", "0xffff", "0o177777", "0b1111111111111111"), + nc(65536, "65536", "0x10000", "0o200000", "0b10000000000000000"), + nc(65537, "65537", "0x10001", "0o200001", "0b10000000000000001"), + nc(99999, "99999", "0x1869f", "0o303237", "0b11000011010011111"), + nc(100000, "100000", "0x186a0", "0o303240", "0b11000011010100000"), + nc(100001, "100001", "0x186a1", "0o303241", "0b11000011010100001"), + nc(131071, "131071", "0x1ffff", "0o377777", "0b11111111111111111"), + nc(131072, "131072", "0x20000", "0o400000", "0b100000000000000000"), + nc(131073, "131073", "0x20001", "0o400001", "0b100000000000000001"), + nc(214747, "214747", "0x346db", "0o643333", "0b110100011011011011"), + nc(214748, "214748", "0x346dc", "0o643334", "0b110100011011011100"), + nc(214749, "214749", "0x346dd", "0o643335", "0b110100011011011101"), + nc(262143, "262143", "0x3ffff", "0o777777", "0b111111111111111111"), + nc(262144, "262144", "0x40000", "0o1000000", "0b1000000000000000000"), + nc(262145, "262145", "0x40001", "0o1000001", "0b1000000000000000001"), + nc(524287, "524287", "0x7ffff", "0o1777777", "0b1111111111111111111"), + nc(524288, "524288", "0x80000", "0o2000000", "0b10000000000000000000"), + nc(524289, "524289", "0x80001", "0o2000001", "0b10000000000000000001"), + nc(999999, "999999", "0xf423f", "0o3641077", "0b11110100001000111111"), + nc(1000000, "1000000", "0xf4240", "0o3641100", "0b11110100001001000000"), + nc(1000001, "1000001", "0xf4241", "0o3641101", "0b11110100001001000001"), + nc(1048575, "1048575", "0xfffff", "0o3777777", "0b11111111111111111111"), + nc(1048576, "1048576", "0x100000", "0o4000000", "0b100000000000000000000"), + nc(1048577, "1048577", "0x100001", "0o4000001", "0b100000000000000000001"), + nc(2097151, "2097151", "0x1fffff", "0o7777777", "0b111111111111111111111"), + nc(2097152, "2097152", "0x200000", "0o10000000", "0b1000000000000000000000"), + nc(2097153, "2097153", "0x200001", "0o10000001", "0b1000000000000000000001"), + nc(2147482, "2147482", "0x20c49a", "0o10142232", "0b1000001100010010011010"), + nc(2147483, "2147483", "0x20c49b", "0o10142233", "0b1000001100010010011011"), + nc(2147484, "2147484", "0x20c49c", "0o10142234", "0b1000001100010010011100"), + nc(4194303, "4194303", "0x3fffff", "0o17777777", "0b1111111111111111111111"), + nc(4194304, "4194304", "0x400000", "0o20000000", "0b10000000000000000000000"), + nc(4194305, "4194305", "0x400001", "0o20000001", "0b10000000000000000000001"), + nc(8388607, "8388607", "0x7fffff", "0o37777777", "0b11111111111111111111111"), + nc(8388608, "8388608", "0x800000", "0o40000000", "0b100000000000000000000000"), + nc(8388609, "8388609", "0x800001", "0o40000001", "0b100000000000000000000001"), + nc(9999999, "9999999", "0x98967f", "0o46113177", "0b100110001001011001111111"), + nc(10000000, "10000000", "0x989680", "0o46113200", "0b100110001001011010000000"), + nc(10000001, "10000001", "0x989681", "0o46113201", "0b100110001001011010000001"), + nc(16777215, "16777215", "0xffffff", "0o77777777", "0b111111111111111111111111"), + nc(16777216, "16777216", "0x1000000", "0o100000000", "0b1000000000000000000000000"), + nc(16777217, "16777217", "0x1000001", "0o100000001", "0b1000000000000000000000001"), + nc(21474835, "21474835", "0x147ae13", "0o121727023", "0b1010001111010111000010011"), + nc(21474836, "21474836", "0x147ae14", "0o121727024", "0b1010001111010111000010100"), + nc(21474837, "21474837", "0x147ae15", "0o121727025", "0b1010001111010111000010101"), + nc(33554431, "33554431", "0x1ffffff", "0o177777777", "0b1111111111111111111111111"), + nc(33554432, "33554432", "0x2000000", "0o200000000", "0b10000000000000000000000000"), + nc(33554433, "33554433", "0x2000001", "0o200000001", "0b10000000000000000000000001"), + nc(67108863, "67108863", "0x3ffffff", "0o377777777", "0b11111111111111111111111111"), + nc(67108864, "67108864", "0x4000000", "0o400000000", "0b100000000000000000000000000"), + nc(67108865, "67108865", "0x4000001", "0o400000001", "0b100000000000000000000000001"), + nc(99999999, "99999999", "0x5f5e0ff", "0o575360377", "0b101111101011110000011111111"), + nc(100000000, "100000000", "0x5f5e100", "0o575360400", "0b101111101011110000100000000"), + nc(100000001, "100000001", "0x5f5e101", "0o575360401", "0b101111101011110000100000001"), + nc(134217727, "134217727", "0x7ffffff", "0o777777777", "0b111111111111111111111111111"), + nc(134217728, "134217728", "0x8000000", "0o1000000000", "0b1000000000000000000000000000"), + nc(134217729, "134217729", "0x8000001", "0o1000000001", "0b1000000000000000000000000001"), + nc(214748363, "214748363", "0xccccccb", "0o1463146313", "0b1100110011001100110011001011"), + nc(214748364, "214748364", "0xccccccc", "0o1463146314", "0b1100110011001100110011001100"), + nc(214748365, "214748365", "0xccccccd", "0o1463146315", "0b1100110011001100110011001101"), + nc(268435455, "268435455", "0xfffffff", "0o1777777777", "0b1111111111111111111111111111"), + nc(268435456, "268435456", "0x10000000", "0o2000000000", "0b10000000000000000000000000000"), + nc(268435457, "268435457", "0x10000001", "0o2000000001", "0b10000000000000000000000000001"), + nc(536870911, "536870911", "0x1fffffff", "0o3777777777", "0b11111111111111111111111111111"), + nc(536870912, "536870912", "0x20000000", "0o4000000000", "0b100000000000000000000000000000"), + nc(536870913, "536870913", "0x20000001", "0o4000000001", "0b100000000000000000000000000001"), + nc(999999999, "999999999", "0x3b9ac9ff", "0o7346544777", "0b111011100110101100100111111111"), + nc(1000000000, "1000000000", "0x3b9aca00", "0o7346545000", "0b111011100110101100101000000000"), + nc(1000000001, "1000000001", "0x3b9aca01", "0o7346545001", "0b111011100110101100101000000001"), + nc(1073741823, "1073741823", "0x3fffffff", "0o7777777777", "0b111111111111111111111111111111"), + nc(1073741824, "1073741824", "0x40000000", "0o10000000000", "0b1000000000000000000000000000000"), + nc(1073741825, "1073741825", "0x40000001", "0o10000000001", "0b1000000000000000000000000000001"), + nc(2147483642, "2147483642", "0x7ffffffa", "0o17777777772", "0b1111111111111111111111111111010"), + nc(2147483643, "2147483643", "0x7ffffffb", "0o17777777773", "0b1111111111111111111111111111011"), + nc(2147483644, "2147483644", "0x7ffffffc", "0o17777777774", "0b1111111111111111111111111111100"), + nc(2147483645, "2147483645", "0x7ffffffd", "0o17777777775", "0b1111111111111111111111111111101"), + nc(2147483646, "2147483646", "0x7ffffffe", "0o17777777776", "0b1111111111111111111111111111110"), + nc(2147483647, "2147483647", "0x7fffffff", "0o17777777777", "0b1111111111111111111111111111111"), +#undef nc + }; +}; + + +template<> +struct numbers<uint32_t> +{ + using value_type = uint32_t; + static C4_INLINE_CONSTEXPR const number_case<uint32_t> vals[] = { +#define nc(val, dec, hex, bin, oct) \ + number_case<value_type>{(value_type)UINT32_C(val), csubstr{dec}, csubstr{hex}, csubstr{bin}, csubstr{oct}} + nc(0, "0", "0x0", "0o0", "0b0"), + nc(1, "1", "0x1", "0o1", "0b1"), + nc(2, "2", "0x2", "0o2", "0b10"), + nc(3, "3", "0x3", "0o3", "0b11"), + nc(4, "4", "0x4", "0o4", "0b100"), + nc(5, "5", "0x5", "0o5", "0b101"), + nc(6, "6", "0x6", "0o6", "0b110"), + nc(7, "7", "0x7", "0o7", "0b111"), + nc(8, "8", "0x8", "0o10", "0b1000"), + nc(9, "9", "0x9", "0o11", "0b1001"), + nc(10, "10", "0xa", "0o12", "0b1010"), + nc(11, "11", "0xb", "0o13", "0b1011"), + nc(15, "15", "0xf", "0o17", "0b1111"), + nc(16, "16", "0x10", "0o20", "0b10000"), + nc(17, "17", "0x11", "0o21", "0b10001"), + nc(31, "31", "0x1f", "0o37", "0b11111"), + nc(32, "32", "0x20", "0o40", "0b100000"), + nc(33, "33", "0x21", "0o41", "0b100001"), + nc(41, "41", "0x29", "0o51", "0b101001"), + nc(42, "42", "0x2a", "0o52", "0b101010"), + nc(43, "43", "0x2b", "0o53", "0b101011"), + nc(63, "63", "0x3f", "0o77", "0b111111"), + nc(64, "64", "0x40", "0o100", "0b1000000"), + nc(65, "65", "0x41", "0o101", "0b1000001"), + nc(99, "99", "0x63", "0o143", "0b1100011"), + nc(100, "100", "0x64", "0o144", "0b1100100"), + nc(101, "101", "0x65", "0o145", "0b1100101"), + nc(127, "127", "0x7f", "0o177", "0b1111111"), + nc(128, "128", "0x80", "0o200", "0b10000000"), + nc(129, "129", "0x81", "0o201", "0b10000001"), + nc(255, "255", "0xff", "0o377", "0b11111111"), + nc(256, "256", "0x100", "0o400", "0b100000000"), + nc(257, "257", "0x101", "0o401", "0b100000001"), + nc(428, "428", "0x1ac", "0o654", "0b110101100"), + nc(429, "429", "0x1ad", "0o655", "0b110101101"), + nc(430, "430", "0x1ae", "0o656", "0b110101110"), + nc(511, "511", "0x1ff", "0o777", "0b111111111"), + nc(512, "512", "0x200", "0o1000", "0b1000000000"), + nc(513, "513", "0x201", "0o1001", "0b1000000001"), + nc(999, "999", "0x3e7", "0o1747", "0b1111100111"), + nc(1000, "1000", "0x3e8", "0o1750", "0b1111101000"), + nc(1001, "1001", "0x3e9", "0o1751", "0b1111101001"), + nc(1023, "1023", "0x3ff", "0o1777", "0b1111111111"), + nc(1024, "1024", "0x400", "0o2000", "0b10000000000"), + nc(1025, "1025", "0x401", "0o2001", "0b10000000001"), + nc(2047, "2047", "0x7ff", "0o3777", "0b11111111111"), + nc(2048, "2048", "0x800", "0o4000", "0b100000000000"), + nc(2049, "2049", "0x801", "0o4001", "0b100000000001"), + nc(4095, "4095", "0xfff", "0o7777", "0b111111111111"), + nc(4096, "4096", "0x1000", "0o10000", "0b1000000000000"), + nc(4097, "4097", "0x1001", "0o10001", "0b1000000000001"), + nc(4293, "4293", "0x10c5", "0o10305", "0b1000011000101"), + nc(4294, "4294", "0x10c6", "0o10306", "0b1000011000110"), + nc(4295, "4295", "0x10c7", "0o10307", "0b1000011000111"), + nc(8191, "8191", "0x1fff", "0o17777", "0b1111111111111"), + nc(8192, "8192", "0x2000", "0o20000", "0b10000000000000"), + nc(8193, "8193", "0x2001", "0o20001", "0b10000000000001"), + nc(9999, "9999", "0x270f", "0o23417", "0b10011100001111"), + nc(10000, "10000", "0x2710", "0o23420", "0b10011100010000"), + nc(10001, "10001", "0x2711", "0o23421", "0b10011100010001"), + nc(16383, "16383", "0x3fff", "0o37777", "0b11111111111111"), + nc(16384, "16384", "0x4000", "0o40000", "0b100000000000000"), + nc(16385, "16385", "0x4001", "0o40001", "0b100000000000001"), + nc(32767, "32767", "0x7fff", "0o77777", "0b111111111111111"), + nc(32768, "32768", "0x8000", "0o100000", "0b1000000000000000"), + nc(32769, "32769", "0x8001", "0o100001", "0b1000000000000001"), + nc(42948, "42948", "0xa7c4", "0o123704", "0b1010011111000100"), + nc(42949, "42949", "0xa7c5", "0o123705", "0b1010011111000101"), + nc(42950, "42950", "0xa7c6", "0o123706", "0b1010011111000110"), + nc(65535, "65535", "0xffff", "0o177777", "0b1111111111111111"), + nc(65536, "65536", "0x10000", "0o200000", "0b10000000000000000"), + nc(65537, "65537", "0x10001", "0o200001", "0b10000000000000001"), + nc(99999, "99999", "0x1869f", "0o303237", "0b11000011010011111"), + nc(100000, "100000", "0x186a0", "0o303240", "0b11000011010100000"), + nc(100001, "100001", "0x186a1", "0o303241", "0b11000011010100001"), + nc(131071, "131071", "0x1ffff", "0o377777", "0b11111111111111111"), + nc(131072, "131072", "0x20000", "0o400000", "0b100000000000000000"), + nc(131073, "131073", "0x20001", "0o400001", "0b100000000000000001"), + nc(262143, "262143", "0x3ffff", "0o777777", "0b111111111111111111"), + nc(262144, "262144", "0x40000", "0o1000000", "0b1000000000000000000"), + nc(262145, "262145", "0x40001", "0o1000001", "0b1000000000000000001"), + nc(429495, "429495", "0x68db7", "0o1506667", "0b1101000110110110111"), + nc(429496, "429496", "0x68db8", "0o1506670", "0b1101000110110111000"), + nc(429497, "429497", "0x68db9", "0o1506671", "0b1101000110110111001"), + nc(524287, "524287", "0x7ffff", "0o1777777", "0b1111111111111111111"), + nc(524288, "524288", "0x80000", "0o2000000", "0b10000000000000000000"), + nc(524289, "524289", "0x80001", "0o2000001", "0b10000000000000000001"), + nc(999999, "999999", "0xf423f", "0o3641077", "0b11110100001000111111"), + nc(1000000, "1000000", "0xf4240", "0o3641100", "0b11110100001001000000"), + nc(1000001, "1000001", "0xf4241", "0o3641101", "0b11110100001001000001"), + nc(1048575, "1048575", "0xfffff", "0o3777777", "0b11111111111111111111"), + nc(1048576, "1048576", "0x100000", "0o4000000", "0b100000000000000000000"), + nc(1048577, "1048577", "0x100001", "0o4000001", "0b100000000000000000001"), + nc(2097151, "2097151", "0x1fffff", "0o7777777", "0b111111111111111111111"), + nc(2097152, "2097152", "0x200000", "0o10000000", "0b1000000000000000000000"), + nc(2097153, "2097153", "0x200001", "0o10000001", "0b1000000000000000000001"), + nc(4194303, "4194303", "0x3fffff", "0o17777777", "0b1111111111111111111111"), + nc(4194304, "4194304", "0x400000", "0o20000000", "0b10000000000000000000000"), + nc(4194305, "4194305", "0x400001", "0o20000001", "0b10000000000000000000001"), + nc(4294966, "4294966", "0x418936", "0o20304466", "0b10000011000100100110110"), + nc(4294967, "4294967", "0x418937", "0o20304467", "0b10000011000100100110111"), + nc(4294968, "4294968", "0x418938", "0o20304470", "0b10000011000100100111000"), + nc(8388607, "8388607", "0x7fffff", "0o37777777", "0b11111111111111111111111"), + nc(8388608, "8388608", "0x800000", "0o40000000", "0b100000000000000000000000"), + nc(8388609, "8388609", "0x800001", "0o40000001", "0b100000000000000000000001"), + nc(9999999, "9999999", "0x98967f", "0o46113177", "0b100110001001011001111111"), + nc(10000000, "10000000", "0x989680", "0o46113200", "0b100110001001011010000000"), + nc(10000001, "10000001", "0x989681", "0o46113201", "0b100110001001011010000001"), + nc(16777215, "16777215", "0xffffff", "0o77777777", "0b111111111111111111111111"), + nc(16777216, "16777216", "0x1000000", "0o100000000", "0b1000000000000000000000000"), + nc(16777217, "16777217", "0x1000001", "0o100000001", "0b1000000000000000000000001"), + nc(33554431, "33554431", "0x1ffffff", "0o177777777", "0b1111111111111111111111111"), + nc(33554432, "33554432", "0x2000000", "0o200000000", "0b10000000000000000000000000"), + nc(33554433, "33554433", "0x2000001", "0o200000001", "0b10000000000000000000000001"), + nc(42949671, "42949671", "0x28f5c27", "0o243656047", "0b10100011110101110000100111"), + nc(42949672, "42949672", "0x28f5c28", "0o243656050", "0b10100011110101110000101000"), + nc(42949673, "42949673", "0x28f5c29", "0o243656051", "0b10100011110101110000101001"), + nc(67108863, "67108863", "0x3ffffff", "0o377777777", "0b11111111111111111111111111"), + nc(67108864, "67108864", "0x4000000", "0o400000000", "0b100000000000000000000000000"), + nc(67108865, "67108865", "0x4000001", "0o400000001", "0b100000000000000000000000001"), + nc(99999999, "99999999", "0x5f5e0ff", "0o575360377", "0b101111101011110000011111111"), + nc(100000000, "100000000", "0x5f5e100", "0o575360400", "0b101111101011110000100000000"), + nc(100000001, "100000001", "0x5f5e101", "0o575360401", "0b101111101011110000100000001"), + nc(134217727, "134217727", "0x7ffffff", "0o777777777", "0b111111111111111111111111111"), + nc(134217728, "134217728", "0x8000000", "0o1000000000", "0b1000000000000000000000000000"), + nc(134217729, "134217729", "0x8000001", "0o1000000001", "0b1000000000000000000000000001"), + nc(268435455, "268435455", "0xfffffff", "0o1777777777", "0b1111111111111111111111111111"), + nc(268435456, "268435456", "0x10000000", "0o2000000000", "0b10000000000000000000000000000"), + nc(268435457, "268435457", "0x10000001", "0o2000000001", "0b10000000000000000000000000001"), + nc(429496728, "429496728", "0x19999998", "0o3146314630", "0b11001100110011001100110011000"), + nc(429496729, "429496729", "0x19999999", "0o3146314631", "0b11001100110011001100110011001"), + nc(429496730, "429496730", "0x1999999a", "0o3146314632", "0b11001100110011001100110011010"), + nc(536870911, "536870911", "0x1fffffff", "0o3777777777", "0b11111111111111111111111111111"), + nc(536870912, "536870912", "0x20000000", "0o4000000000", "0b100000000000000000000000000000"), + nc(536870913, "536870913", "0x20000001", "0o4000000001", "0b100000000000000000000000000001"), + nc(999999999, "999999999", "0x3b9ac9ff", "0o7346544777", "0b111011100110101100100111111111"), + nc(1000000000, "1000000000", "0x3b9aca00", "0o7346545000", "0b111011100110101100101000000000"), + nc(1000000001, "1000000001", "0x3b9aca01", "0o7346545001", "0b111011100110101100101000000001"), + nc(1073741823, "1073741823", "0x3fffffff", "0o7777777777", "0b111111111111111111111111111111"), + nc(1073741824, "1073741824", "0x40000000", "0o10000000000", "0b1000000000000000000000000000000"), + nc(1073741825, "1073741825", "0x40000001", "0o10000000001", "0b1000000000000000000000000000001"), + nc(2147483647, "2147483647", "0x7fffffff", "0o17777777777", "0b1111111111111111111111111111111"), + nc(2147483648, "2147483648", "0x80000000", "0o20000000000", "0b10000000000000000000000000000000"), + nc(2147483649, "2147483649", "0x80000001", "0o20000000001", "0b10000000000000000000000000000001"), + nc(4294967291, "4294967291", "0xfffffffb", "0o37777777773", "0b11111111111111111111111111111011"), + nc(4294967292, "4294967292", "0xfffffffc", "0o37777777774", "0b11111111111111111111111111111100"), + nc(4294967293, "4294967293", "0xfffffffd", "0o37777777775", "0b11111111111111111111111111111101"), + nc(4294967294, "4294967294", "0xfffffffe", "0o37777777776", "0b11111111111111111111111111111110"), + nc(4294967295, "4294967295", "0xffffffff", "0o37777777777", "0b11111111111111111111111111111111"), +#undef nc + }; +}; + + +template<> +struct numbers<int64_t> +{ + using value_type = int64_t; + static C4_INLINE_CONSTEXPR const number_case<int64_t> vals[] = { +#define nc(val, dec, hex, bin, oct) \ + number_case<value_type>{(value_type)INT64_C(val), csubstr{dec}, csubstr{hex}, csubstr{bin}, csubstr{oct}} +#define ncm1(val, dec, hex, bin, oct) \ + number_case<value_type>{(value_type)(INT64_C(val)-INT64_C(1)), csubstr{dec}, csubstr{hex}, csubstr{bin}, csubstr{oct}} + ncm1(-9223372036854775807, "-9223372036854775808", "-0x8000000000000000", "-0o1000000000000000000000", "-0b1000000000000000000000000000000000000000000000000000000000000000"), + nc(-9223372036854775807, "-9223372036854775807", "-0x7fffffffffffffff", "-0o777777777777777777777", "-0b111111111111111111111111111111111111111111111111111111111111111"), + nc(-9223372036854775806, "-9223372036854775806", "-0x7ffffffffffffffe", "-0o777777777777777777776", "-0b111111111111111111111111111111111111111111111111111111111111110"), + nc(-9223372036854775805, "-9223372036854775805", "-0x7ffffffffffffffd", "-0o777777777777777777775", "-0b111111111111111111111111111111111111111111111111111111111111101"), + nc(-9223372036854775804, "-9223372036854775804", "-0x7ffffffffffffffc", "-0o777777777777777777774", "-0b111111111111111111111111111111111111111111111111111111111111100"), + nc(-9223372036854775803, "-9223372036854775803", "-0x7ffffffffffffffb", "-0o777777777777777777773", "-0b111111111111111111111111111111111111111111111111111111111111011"), + nc(-4611686018427387905, "-4611686018427387905", "-0x4000000000000001", "-0o400000000000000000001", "-0b100000000000000000000000000000000000000000000000000000000000001"), + nc(-4611686018427387904, "-4611686018427387904", "-0x4000000000000000", "-0o400000000000000000000", "-0b100000000000000000000000000000000000000000000000000000000000000"), + nc(-4611686018427387903, "-4611686018427387903", "-0x3fffffffffffffff", "-0o377777777777777777777", "-0b11111111111111111111111111111111111111111111111111111111111111"), + nc(-2305843009213693953, "-2305843009213693953", "-0x2000000000000001", "-0o200000000000000000001", "-0b10000000000000000000000000000000000000000000000000000000000001"), + nc(-2305843009213693952, "-2305843009213693952", "-0x2000000000000000", "-0o200000000000000000000", "-0b10000000000000000000000000000000000000000000000000000000000000"), + nc(-2305843009213693951, "-2305843009213693951", "-0x1fffffffffffffff", "-0o177777777777777777777", "-0b1111111111111111111111111111111111111111111111111111111111111"), + nc(-1152921504606846977, "-1152921504606846977", "-0x1000000000000001", "-0o100000000000000000001", "-0b1000000000000000000000000000000000000000000000000000000000001"), + nc(-1152921504606846976, "-1152921504606846976", "-0x1000000000000000", "-0o100000000000000000000", "-0b1000000000000000000000000000000000000000000000000000000000000"), + nc(-1152921504606846975, "-1152921504606846975", "-0xfffffffffffffff", "-0o77777777777777777777", "-0b111111111111111111111111111111111111111111111111111111111111"), + nc(-1000000000000000001, "-1000000000000000001", "-0xde0b6b3a7640001", "-0o67405553164731000001", "-0b110111100000101101101011001110100111011001000000000000000001"), + nc(-1000000000000000000, "-1000000000000000000", "-0xde0b6b3a7640000", "-0o67405553164731000000", "-0b110111100000101101101011001110100111011001000000000000000000"), + nc(-999999999999999999, "-999999999999999999", "-0xde0b6b3a763ffff", "-0o67405553164730777777", "-0b110111100000101101101011001110100111011000111111111111111111"), + nc(-922337203685477632, "-922337203685477632", "-0xccccccccccccd00", "-0o63146314631463146400", "-0b110011001100110011001100110011001100110011001100110100000000"), + nc(-576460752303423489, "-576460752303423489", "-0x800000000000001", "-0o40000000000000000001", "-0b100000000000000000000000000000000000000000000000000000000001"), + nc(-576460752303423488, "-576460752303423488", "-0x800000000000000", "-0o40000000000000000000", "-0b100000000000000000000000000000000000000000000000000000000000"), + nc(-576460752303423487, "-576460752303423487", "-0x7ffffffffffffff", "-0o37777777777777777777", "-0b11111111111111111111111111111111111111111111111111111111111"), + nc(-288230376151711745, "-288230376151711745", "-0x400000000000001", "-0o20000000000000000001", "-0b10000000000000000000000000000000000000000000000000000000001"), + nc(-288230376151711744, "-288230376151711744", "-0x400000000000000", "-0o20000000000000000000", "-0b10000000000000000000000000000000000000000000000000000000000"), + nc(-288230376151711743, "-288230376151711743", "-0x3ffffffffffffff", "-0o17777777777777777777", "-0b1111111111111111111111111111111111111111111111111111111111"), + nc(-144115188075855873, "-144115188075855873", "-0x200000000000001", "-0o10000000000000000001", "-0b1000000000000000000000000000000000000000000000000000000001"), + nc(-144115188075855872, "-144115188075855872", "-0x200000000000000", "-0o10000000000000000000", "-0b1000000000000000000000000000000000000000000000000000000000"), + nc(-144115188075855871, "-144115188075855871", "-0x1ffffffffffffff", "-0o7777777777777777777", "-0b111111111111111111111111111111111111111111111111111111111"), + nc(-100000000000000001, "-100000000000000001", "-0x16345785d8a0001", "-0o5432127413542400001", "-0b101100011010001010111100001011101100010100000000000000001"), + nc(-100000000000000000, "-100000000000000000", "-0x16345785d8a0000", "-0o5432127413542400000", "-0b101100011010001010111100001011101100010100000000000000000"), + nc(-99999999999999999, "-99999999999999999", "-0x16345785d89ffff", "-0o5432127413542377777", "-0b101100011010001010111100001011101100010011111111111111111"), + nc(-92233720368547760, "-92233720368547760", "-0x147ae147ae147b0", "-0o5075341217270243660", "-0b101000111101011100001010001111010111000010100011110110000"), + nc(-72057594037927937, "-72057594037927937", "-0x100000000000001", "-0o4000000000000000001", "-0b100000000000000000000000000000000000000000000000000000001"), + nc(-72057594037927936, "-72057594037927936", "-0x100000000000000", "-0o4000000000000000000", "-0b100000000000000000000000000000000000000000000000000000000"), + nc(-72057594037927935, "-72057594037927935", "-0xffffffffffffff", "-0o3777777777777777777", "-0b11111111111111111111111111111111111111111111111111111111"), + nc(-36028797018963969, "-36028797018963969", "-0x80000000000001", "-0o2000000000000000001", "-0b10000000000000000000000000000000000000000000000000000001"), + nc(-36028797018963968, "-36028797018963968", "-0x80000000000000", "-0o2000000000000000000", "-0b10000000000000000000000000000000000000000000000000000000"), + nc(-36028797018963967, "-36028797018963967", "-0x7fffffffffffff", "-0o1777777777777777777", "-0b1111111111111111111111111111111111111111111111111111111"), + nc(-18014398509481985, "-18014398509481985", "-0x40000000000001", "-0o1000000000000000001", "-0b1000000000000000000000000000000000000000000000000000001"), + nc(-18014398509481984, "-18014398509481984", "-0x40000000000000", "-0o1000000000000000000", "-0b1000000000000000000000000000000000000000000000000000000"), + nc(-18014398509481983, "-18014398509481983", "-0x3fffffffffffff", "-0o777777777777777777", "-0b111111111111111111111111111111111111111111111111111111"), + nc(-10000000000000001, "-10000000000000001", "-0x2386f26fc10001", "-0o434157115760200001", "-0b100011100001101111001001101111110000010000000000000001"), + nc(-10000000000000000, "-10000000000000000", "-0x2386f26fc10000", "-0o434157115760200000", "-0b100011100001101111001001101111110000010000000000000000"), + nc(-9999999999999999, "-9999999999999999", "-0x2386f26fc0ffff", "-0o434157115760177777", "-0b100011100001101111001001101111110000001111111111111111"), + nc(-9223372036854776, "-9223372036854776", "-0x20c49ba5e353f8", "-0o406111564570651770", "-0b100000110001001001101110100101111000110101001111111000"), + nc(-9007199254740993, "-9007199254740993", "-0x20000000000001", "-0o400000000000000001", "-0b100000000000000000000000000000000000000000000000000001"), + nc(-9007199254740992, "-9007199254740992", "-0x20000000000000", "-0o400000000000000000", "-0b100000000000000000000000000000000000000000000000000000"), + nc(-9007199254740991, "-9007199254740991", "-0x1fffffffffffff", "-0o377777777777777777", "-0b11111111111111111111111111111111111111111111111111111"), + nc(-4503599627370497, "-4503599627370497", "-0x10000000000001", "-0o200000000000000001", "-0b10000000000000000000000000000000000000000000000000001"), + nc(-4503599627370496, "-4503599627370496", "-0x10000000000000", "-0o200000000000000000", "-0b10000000000000000000000000000000000000000000000000000"), + nc(-4503599627370495, "-4503599627370495", "-0xfffffffffffff", "-0o177777777777777777", "-0b1111111111111111111111111111111111111111111111111111"), + nc(-2251799813685249, "-2251799813685249", "-0x8000000000001", "-0o100000000000000001", "-0b1000000000000000000000000000000000000000000000000001"), + nc(-2251799813685248, "-2251799813685248", "-0x8000000000000", "-0o100000000000000000", "-0b1000000000000000000000000000000000000000000000000000"), + nc(-2251799813685247, "-2251799813685247", "-0x7ffffffffffff", "-0o77777777777777777", "-0b111111111111111111111111111111111111111111111111111"), + nc(-1125899906842625, "-1125899906842625", "-0x4000000000001", "-0o40000000000000001", "-0b100000000000000000000000000000000000000000000000001"), + nc(-1125899906842624, "-1125899906842624", "-0x4000000000000", "-0o40000000000000000", "-0b100000000000000000000000000000000000000000000000000"), + nc(-1125899906842623, "-1125899906842623", "-0x3ffffffffffff", "-0o37777777777777777", "-0b11111111111111111111111111111111111111111111111111"), + nc(-1000000000000001, "-1000000000000001", "-0x38d7ea4c68001", "-0o34327724461500001", "-0b11100011010111111010100100110001101000000000000001"), + nc(-1000000000000000, "-1000000000000000", "-0x38d7ea4c68000", "-0o34327724461500000", "-0b11100011010111111010100100110001101000000000000000"), + nc(-999999999999999, "-999999999999999", "-0x38d7ea4c67fff", "-0o34327724461477777", "-0b11100011010111111010100100110001100111111111111111"), + nc(-922337203685477, "-922337203685477", "-0x346dc5d638865", "-0o32155613530704145", "-0b11010001101101110001011101011000111000100001100101"), + nc(-562949953421313, "-562949953421313", "-0x2000000000001", "-0o20000000000000001", "-0b10000000000000000000000000000000000000000000000001"), + nc(-562949953421312, "-562949953421312", "-0x2000000000000", "-0o20000000000000000", "-0b10000000000000000000000000000000000000000000000000"), + nc(-562949953421311, "-562949953421311", "-0x1ffffffffffff", "-0o17777777777777777", "-0b1111111111111111111111111111111111111111111111111"), + nc(-281474976710657, "-281474976710657", "-0x1000000000001", "-0o10000000000000001", "-0b1000000000000000000000000000000000000000000000001"), + nc(-281474976710656, "-281474976710656", "-0x1000000000000", "-0o10000000000000000", "-0b1000000000000000000000000000000000000000000000000"), + nc(-281474976710655, "-281474976710655", "-0xffffffffffff", "-0o7777777777777777", "-0b111111111111111111111111111111111111111111111111"), + nc(-140737488355329, "-140737488355329", "-0x800000000001", "-0o4000000000000001", "-0b100000000000000000000000000000000000000000000001"), + nc(-140737488355328, "-140737488355328", "-0x800000000000", "-0o4000000000000000", "-0b100000000000000000000000000000000000000000000000"), + nc(-140737488355327, "-140737488355327", "-0x7fffffffffff", "-0o3777777777777777", "-0b11111111111111111111111111111111111111111111111"), + nc(-100000000000001, "-100000000000001", "-0x5af3107a4001", "-0o2657142036440001", "-0b10110101111001100010000011110100100000000000001"), + nc(-100000000000000, "-100000000000000", "-0x5af3107a4000", "-0o2657142036440000", "-0b10110101111001100010000011110100100000000000000"), + nc(-99999999999999, "-99999999999999", "-0x5af3107a3fff", "-0o2657142036437777", "-0b10110101111001100010000011110100011111111111111"), + nc(-92233720368547, "-92233720368547", "-0x53e2d6238da3", "-0o2476132610706643", "-0b10100111110001011010110001000111000110110100011"), + nc(-70368744177665, "-70368744177665", "-0x400000000001", "-0o2000000000000001", "-0b10000000000000000000000000000000000000000000001"), + nc(-70368744177664, "-70368744177664", "-0x400000000000", "-0o2000000000000000", "-0b10000000000000000000000000000000000000000000000"), + nc(-70368744177663, "-70368744177663", "-0x3fffffffffff", "-0o1777777777777777", "-0b1111111111111111111111111111111111111111111111"), + nc(-35184372088833, "-35184372088833", "-0x200000000001", "-0o1000000000000001", "-0b1000000000000000000000000000000000000000000001"), + nc(-35184372088832, "-35184372088832", "-0x200000000000", "-0o1000000000000000", "-0b1000000000000000000000000000000000000000000000"), + nc(-35184372088831, "-35184372088831", "-0x1fffffffffff", "-0o777777777777777", "-0b111111111111111111111111111111111111111111111"), + nc(-17592186044417, "-17592186044417", "-0x100000000001", "-0o400000000000001", "-0b100000000000000000000000000000000000000000001"), + nc(-17592186044416, "-17592186044416", "-0x100000000000", "-0o400000000000000", "-0b100000000000000000000000000000000000000000000"), + nc(-17592186044415, "-17592186044415", "-0xfffffffffff", "-0o377777777777777", "-0b11111111111111111111111111111111111111111111"), + nc(-10000000000001, "-10000000000001", "-0x9184e72a001", "-0o221411634520001", "-0b10010001100001001110011100101010000000000001"), + nc(-10000000000000, "-10000000000000", "-0x9184e72a000", "-0o221411634520000", "-0b10010001100001001110011100101010000000000000"), + nc(-9999999999999, "-9999999999999", "-0x9184e729fff", "-0o221411634517777", "-0b10010001100001001110011100101001111111111111"), + nc(-9223372036854, "-9223372036854", "-0x8637bd05af6", "-0o206157364055366", "-0b10000110001101111011110100000101101011110110"), + nc(-8796093022209, "-8796093022209", "-0x80000000001", "-0o200000000000001", "-0b10000000000000000000000000000000000000000001"), + nc(-8796093022208, "-8796093022208", "-0x80000000000", "-0o200000000000000", "-0b10000000000000000000000000000000000000000000"), + nc(-8796093022207, "-8796093022207", "-0x7ffffffffff", "-0o177777777777777", "-0b1111111111111111111111111111111111111111111"), + nc(-4398046511105, "-4398046511105", "-0x40000000001", "-0o100000000000001", "-0b1000000000000000000000000000000000000000001"), + nc(-4398046511104, "-4398046511104", "-0x40000000000", "-0o100000000000000", "-0b1000000000000000000000000000000000000000000"), + nc(-4398046511103, "-4398046511103", "-0x3ffffffffff", "-0o77777777777777", "-0b111111111111111111111111111111111111111111"), + nc(-2199023255553, "-2199023255553", "-0x20000000001", "-0o40000000000001", "-0b100000000000000000000000000000000000000001"), + nc(-2199023255552, "-2199023255552", "-0x20000000000", "-0o40000000000000", "-0b100000000000000000000000000000000000000000"), + nc(-2199023255551, "-2199023255551", "-0x1ffffffffff", "-0o37777777777777", "-0b11111111111111111111111111111111111111111"), + nc(-1099511627777, "-1099511627777", "-0x10000000001", "-0o20000000000001", "-0b10000000000000000000000000000000000000001"), + nc(-1099511627776, "-1099511627776", "-0x10000000000", "-0o20000000000000", "-0b10000000000000000000000000000000000000000"), + nc(-1099511627775, "-1099511627775", "-0xffffffffff", "-0o17777777777777", "-0b1111111111111111111111111111111111111111"), + nc(-1000000000001, "-1000000000001", "-0xe8d4a51001", "-0o16432451210001", "-0b1110100011010100101001010001000000000001"), + nc(-1000000000000, "-1000000000000", "-0xe8d4a51000", "-0o16432451210000", "-0b1110100011010100101001010001000000000000"), + nc(-999999999999, "-999999999999", "-0xe8d4a50fff", "-0o16432451207777", "-0b1110100011010100101001010000111111111111"), + nc(-922337203685, "-922337203685", "-0xd6bf94d5e5", "-0o15327745152745", "-0b1101011010111111100101001101010111100101"), + nc(-549755813889, "-549755813889", "-0x8000000001", "-0o10000000000001", "-0b1000000000000000000000000000000000000001"), + nc(-549755813888, "-549755813888", "-0x8000000000", "-0o10000000000000", "-0b1000000000000000000000000000000000000000"), + nc(-549755813887, "-549755813887", "-0x7fffffffff", "-0o7777777777777", "-0b111111111111111111111111111111111111111"), + nc(-274877906945, "-274877906945", "-0x4000000001", "-0o4000000000001", "-0b100000000000000000000000000000000000001"), + nc(-274877906944, "-274877906944", "-0x4000000000", "-0o4000000000000", "-0b100000000000000000000000000000000000000"), + nc(-274877906943, "-274877906943", "-0x3fffffffff", "-0o3777777777777", "-0b11111111111111111111111111111111111111"), + nc(-137438953473, "-137438953473", "-0x2000000001", "-0o2000000000001", "-0b10000000000000000000000000000000000001"), + nc(-137438953472, "-137438953472", "-0x2000000000", "-0o2000000000000", "-0b10000000000000000000000000000000000000"), + nc(-137438953471, "-137438953471", "-0x1fffffffff", "-0o1777777777777", "-0b1111111111111111111111111111111111111"), + nc(-100000000001, "-100000000001", "-0x174876e801", "-0o1351035564001", "-0b1011101001000011101101110100000000001"), + nc(-100000000000, "-100000000000", "-0x174876e800", "-0o1351035564000", "-0b1011101001000011101101110100000000000"), + nc(-99999999999, "-99999999999", "-0x174876e7ff", "-0o1351035563777", "-0b1011101001000011101101110011111111111"), + nc(-92233720368, "-92233720368", "-0x15798ee230", "-0o1257143561060", "-0b1010101111001100011101110001000110000"), + nc(-68719476737, "-68719476737", "-0x1000000001", "-0o1000000000001", "-0b1000000000000000000000000000000000001"), + nc(-68719476736, "-68719476736", "-0x1000000000", "-0o1000000000000", "-0b1000000000000000000000000000000000000"), + nc(-68719476735, "-68719476735", "-0xfffffffff", "-0o777777777777", "-0b111111111111111111111111111111111111"), + nc(-34359738369, "-34359738369", "-0x800000001", "-0o400000000001", "-0b100000000000000000000000000000000001"), + nc(-34359738368, "-34359738368", "-0x800000000", "-0o400000000000", "-0b100000000000000000000000000000000000"), + nc(-34359738367, "-34359738367", "-0x7ffffffff", "-0o377777777777", "-0b11111111111111111111111111111111111"), + nc(-17179869185, "-17179869185", "-0x400000001", "-0o200000000001", "-0b10000000000000000000000000000000001"), + nc(-17179869184, "-17179869184", "-0x400000000", "-0o200000000000", "-0b10000000000000000000000000000000000"), + nc(-17179869183, "-17179869183", "-0x3ffffffff", "-0o177777777777", "-0b1111111111111111111111111111111111"), + nc(-10000000001, "-10000000001", "-0x2540be401", "-0o112402762001", "-0b1001010100000010111110010000000001"), + nc(-10000000000, "-10000000000", "-0x2540be400", "-0o112402762000", "-0b1001010100000010111110010000000000"), + nc(-9999999999, "-9999999999", "-0x2540be3ff", "-0o112402761777", "-0b1001010100000010111110001111111111"), + nc(-9223372036, "-9223372036", "-0x225c17d04", "-0o104560276404", "-0b1000100101110000010111110100000100"), + nc(-8589934593, "-8589934593", "-0x200000001", "-0o100000000001", "-0b1000000000000000000000000000000001"), + nc(-8589934592, "-8589934592", "-0x200000000", "-0o100000000000", "-0b1000000000000000000000000000000000"), + nc(-8589934591, "-8589934591", "-0x1ffffffff", "-0o77777777777", "-0b111111111111111111111111111111111"), + nc(-4294967297, "-4294967297", "-0x100000001", "-0o40000000001", "-0b100000000000000000000000000000001"), + nc(-4294967296, "-4294967296", "-0x100000000", "-0o40000000000", "-0b100000000000000000000000000000000"), + nc(-4294967295, "-4294967295", "-0xffffffff", "-0o37777777777", "-0b11111111111111111111111111111111"), + nc(-2147483649, "-2147483649", "-0x80000001", "-0o20000000001", "-0b10000000000000000000000000000001"), + nc(-2147483648, "-2147483648", "-0x80000000", "-0o20000000000", "-0b10000000000000000000000000000000"), + nc(-2147483647, "-2147483647", "-0x7fffffff", "-0o17777777777", "-0b1111111111111111111111111111111"), + nc(-1073741825, "-1073741825", "-0x40000001", "-0o10000000001", "-0b1000000000000000000000000000001"), + nc(-1073741824, "-1073741824", "-0x40000000", "-0o10000000000", "-0b1000000000000000000000000000000"), + nc(-1073741823, "-1073741823", "-0x3fffffff", "-0o7777777777", "-0b111111111111111111111111111111"), + nc(-1000000001, "-1000000001", "-0x3b9aca01", "-0o7346545001", "-0b111011100110101100101000000001"), + nc(-1000000000, "-1000000000", "-0x3b9aca00", "-0o7346545000", "-0b111011100110101100101000000000"), + nc(-999999999, "-999999999", "-0x3b9ac9ff", "-0o7346544777", "-0b111011100110101100100111111111"), + nc(-922337203, "-922337203", "-0x36f9bfb3", "-0o6676337663", "-0b110110111110011011111110110011"), + nc(-536870913, "-536870913", "-0x20000001", "-0o4000000001", "-0b100000000000000000000000000001"), + nc(-536870912, "-536870912", "-0x20000000", "-0o4000000000", "-0b100000000000000000000000000000"), + nc(-536870911, "-536870911", "-0x1fffffff", "-0o3777777777", "-0b11111111111111111111111111111"), + nc(-268435457, "-268435457", "-0x10000001", "-0o2000000001", "-0b10000000000000000000000000001"), + nc(-268435456, "-268435456", "-0x10000000", "-0o2000000000", "-0b10000000000000000000000000000"), + nc(-268435455, "-268435455", "-0xfffffff", "-0o1777777777", "-0b1111111111111111111111111111"), + nc(-134217729, "-134217729", "-0x8000001", "-0o1000000001", "-0b1000000000000000000000000001"), + nc(-134217728, "-134217728", "-0x8000000", "-0o1000000000", "-0b1000000000000000000000000000"), + nc(-134217727, "-134217727", "-0x7ffffff", "-0o777777777", "-0b111111111111111111111111111"), + nc(-100000001, "-100000001", "-0x5f5e101", "-0o575360401", "-0b101111101011110000100000001"), + nc(-100000000, "-100000000", "-0x5f5e100", "-0o575360400", "-0b101111101011110000100000000"), + nc(-99999999, "-99999999", "-0x5f5e0ff", "-0o575360377", "-0b101111101011110000011111111"), + nc(-92233720, "-92233720", "-0x57f5ff8", "-0o537657770", "-0b101011111110101111111111000"), + nc(-67108865, "-67108865", "-0x4000001", "-0o400000001", "-0b100000000000000000000000001"), + nc(-67108864, "-67108864", "-0x4000000", "-0o400000000", "-0b100000000000000000000000000"), + nc(-67108863, "-67108863", "-0x3ffffff", "-0o377777777", "-0b11111111111111111111111111"), + nc(-33554433, "-33554433", "-0x2000001", "-0o200000001", "-0b10000000000000000000000001"), + nc(-33554432, "-33554432", "-0x2000000", "-0o200000000", "-0b10000000000000000000000000"), + nc(-33554431, "-33554431", "-0x1ffffff", "-0o177777777", "-0b1111111111111111111111111"), + nc(-16777217, "-16777217", "-0x1000001", "-0o100000001", "-0b1000000000000000000000001"), + nc(-16777216, "-16777216", "-0x1000000", "-0o100000000", "-0b1000000000000000000000000"), + nc(-16777215, "-16777215", "-0xffffff", "-0o77777777", "-0b111111111111111111111111"), + nc(-10000001, "-10000001", "-0x989681", "-0o46113201", "-0b100110001001011010000001"), + nc(-10000000, "-10000000", "-0x989680", "-0o46113200", "-0b100110001001011010000000"), + nc(-9999999, "-9999999", "-0x98967f", "-0o46113177", "-0b100110001001011001111111"), + nc(-9223372, "-9223372", "-0x8cbccc", "-0o43136314", "-0b100011001011110011001100"), + nc(-8388609, "-8388609", "-0x800001", "-0o40000001", "-0b100000000000000000000001"), + nc(-8388608, "-8388608", "-0x800000", "-0o40000000", "-0b100000000000000000000000"), + nc(-8388607, "-8388607", "-0x7fffff", "-0o37777777", "-0b11111111111111111111111"), + nc(-4194305, "-4194305", "-0x400001", "-0o20000001", "-0b10000000000000000000001"), + nc(-4194304, "-4194304", "-0x400000", "-0o20000000", "-0b10000000000000000000000"), + nc(-4194303, "-4194303", "-0x3fffff", "-0o17777777", "-0b1111111111111111111111"), + nc(-2097153, "-2097153", "-0x200001", "-0o10000001", "-0b1000000000000000000001"), + nc(-2097152, "-2097152", "-0x200000", "-0o10000000", "-0b1000000000000000000000"), + nc(-2097151, "-2097151", "-0x1fffff", "-0o7777777", "-0b111111111111111111111"), + nc(-1048577, "-1048577", "-0x100001", "-0o4000001", "-0b100000000000000000001"), + nc(-1048576, "-1048576", "-0x100000", "-0o4000000", "-0b100000000000000000000"), + nc(-1048575, "-1048575", "-0xfffff", "-0o3777777", "-0b11111111111111111111"), + nc(-1000001, "-1000001", "-0xf4241", "-0o3641101", "-0b11110100001001000001"), + nc(-1000000, "-1000000", "-0xf4240", "-0o3641100", "-0b11110100001001000000"), + nc(-999999, "-999999", "-0xf423f", "-0o3641077", "-0b11110100001000111111"), + nc(-922337, "-922337", "-0xe12e1", "-0o3411341", "-0b11100001001011100001"), + nc(-524289, "-524289", "-0x80001", "-0o2000001", "-0b10000000000000000001"), + nc(-524288, "-524288", "-0x80000", "-0o2000000", "-0b10000000000000000000"), + nc(-524287, "-524287", "-0x7ffff", "-0o1777777", "-0b1111111111111111111"), + nc(-262145, "-262145", "-0x40001", "-0o1000001", "-0b1000000000000000001"), + nc(-262144, "-262144", "-0x40000", "-0o1000000", "-0b1000000000000000000"), + nc(-262143, "-262143", "-0x3ffff", "-0o777777", "-0b111111111111111111"), + nc(-131073, "-131073", "-0x20001", "-0o400001", "-0b100000000000000001"), + nc(-131072, "-131072", "-0x20000", "-0o400000", "-0b100000000000000000"), + nc(-131071, "-131071", "-0x1ffff", "-0o377777", "-0b11111111111111111"), + nc(-100001, "-100001", "-0x186a1", "-0o303241", "-0b11000011010100001"), + nc(-100000, "-100000", "-0x186a0", "-0o303240", "-0b11000011010100000"), + nc(-99999, "-99999", "-0x1869f", "-0o303237", "-0b11000011010011111"), + nc(-92233, "-92233", "-0x16849", "-0o264111", "-0b10110100001001001"), + nc(-65537, "-65537", "-0x10001", "-0o200001", "-0b10000000000000001"), + nc(-65536, "-65536", "-0x10000", "-0o200000", "-0b10000000000000000"), + nc(-65535, "-65535", "-0xffff", "-0o177777", "-0b1111111111111111"), + nc(-32769, "-32769", "-0x8001", "-0o100001", "-0b1000000000000001"), + nc(-32768, "-32768", "-0x8000", "-0o100000", "-0b1000000000000000"), + nc(-32767, "-32767", "-0x7fff", "-0o77777", "-0b111111111111111"), + nc(-16385, "-16385", "-0x4001", "-0o40001", "-0b100000000000001"), + nc(-16384, "-16384", "-0x4000", "-0o40000", "-0b100000000000000"), + nc(-16383, "-16383", "-0x3fff", "-0o37777", "-0b11111111111111"), + nc(-10001, "-10001", "-0x2711", "-0o23421", "-0b10011100010001"), + nc(-10000, "-10000", "-0x2710", "-0o23420", "-0b10011100010000"), + nc(-9999, "-9999", "-0x270f", "-0o23417", "-0b10011100001111"), + nc(-9223, "-9223", "-0x2407", "-0o22007", "-0b10010000000111"), + nc(-8193, "-8193", "-0x2001", "-0o20001", "-0b10000000000001"), + nc(-8192, "-8192", "-0x2000", "-0o20000", "-0b10000000000000"), + nc(-8191, "-8191", "-0x1fff", "-0o17777", "-0b1111111111111"), + nc(-4097, "-4097", "-0x1001", "-0o10001", "-0b1000000000001"), + nc(-4096, "-4096", "-0x1000", "-0o10000", "-0b1000000000000"), + nc(-4095, "-4095", "-0xfff", "-0o7777", "-0b111111111111"), + nc(-2049, "-2049", "-0x801", "-0o4001", "-0b100000000001"), + nc(-2048, "-2048", "-0x800", "-0o4000", "-0b100000000000"), + nc(-2047, "-2047", "-0x7ff", "-0o3777", "-0b11111111111"), + nc(-1025, "-1025", "-0x401", "-0o2001", "-0b10000000001"), + nc(-1024, "-1024", "-0x400", "-0o2000", "-0b10000000000"), + nc(-1023, "-1023", "-0x3ff", "-0o1777", "-0b1111111111"), + nc(-1001, "-1001", "-0x3e9", "-0o1751", "-0b1111101001"), + nc(-1000, "-1000", "-0x3e8", "-0o1750", "-0b1111101000"), + nc(-999, "-999", "-0x3e7", "-0o1747", "-0b1111100111"), + nc(-922, "-922", "-0x39a", "-0o1632", "-0b1110011010"), + nc(-513, "-513", "-0x201", "-0o1001", "-0b1000000001"), + nc(-512, "-512", "-0x200", "-0o1000", "-0b1000000000"), + nc(-511, "-511", "-0x1ff", "-0o777", "-0b111111111"), + nc(-257, "-257", "-0x101", "-0o401", "-0b100000001"), + nc(-256, "-256", "-0x100", "-0o400", "-0b100000000"), + nc(-255, "-255", "-0xff", "-0o377", "-0b11111111"), + nc(-129, "-129", "-0x81", "-0o201", "-0b10000001"), + nc(-128, "-128", "-0x80", "-0o200", "-0b10000000"), + nc(-127, "-127", "-0x7f", "-0o177", "-0b1111111"), + nc(-101, "-101", "-0x65", "-0o145", "-0b1100101"), + nc(-100, "-100", "-0x64", "-0o144", "-0b1100100"), + nc(-99, "-99", "-0x63", "-0o143", "-0b1100011"), + nc(-92, "-92", "-0x5c", "-0o134", "-0b1011100"), + nc(-65, "-65", "-0x41", "-0o101", "-0b1000001"), + nc(-64, "-64", "-0x40", "-0o100", "-0b1000000"), + nc(-63, "-63", "-0x3f", "-0o77", "-0b111111"), + nc(-33, "-33", "-0x21", "-0o41", "-0b100001"), + nc(-32, "-32", "-0x20", "-0o40", "-0b100000"), + nc(-31, "-31", "-0x1f", "-0o37", "-0b11111"), + nc(-17, "-17", "-0x11", "-0o21", "-0b10001"), + nc(-16, "-16", "-0x10", "-0o20", "-0b10000"), + nc(-15, "-15", "-0xf", "-0o17", "-0b1111"), + nc(-11, "-11", "-0xb", "-0o13", "-0b1011"), + nc(-10, "-10", "-0xa", "-0o12", "-0b1010"), + nc(-9, "-9", "-0x9", "-0o11", "-0b1001"), + nc(-8, "-8", "-0x8", "-0o10", "-0b1000"), + nc(-7, "-7", "-0x7", "-0o7", "-0b111"), + nc(-6, "-6", "-0x6", "-0o6", "-0b110"), + nc(-5, "-5", "-0x5", "-0o5", "-0b101"), + nc(-4, "-4", "-0x4", "-0o4", "-0b100"), + nc(-3, "-3", "-0x3", "-0o3", "-0b11"), + nc(-2, "-2", "-0x2", "-0o2", "-0b10"), + nc(-1, "-1", "-0x1", "-0o1", "-0b1"), + nc(0, "0", "0x0", "0o0", "0b0"), + nc(1, "1", "0x1", "0o1", "0b1"), + nc(2, "2", "0x2", "0o2", "0b10"), + nc(3, "3", "0x3", "0o3", "0b11"), + nc(4, "4", "0x4", "0o4", "0b100"), + nc(5, "5", "0x5", "0o5", "0b101"), + nc(6, "6", "0x6", "0o6", "0b110"), + nc(7, "7", "0x7", "0o7", "0b111"), + nc(8, "8", "0x8", "0o10", "0b1000"), + nc(9, "9", "0x9", "0o11", "0b1001"), + nc(10, "10", "0xa", "0o12", "0b1010"), + nc(11, "11", "0xb", "0o13", "0b1011"), + nc(15, "15", "0xf", "0o17", "0b1111"), + nc(16, "16", "0x10", "0o20", "0b10000"), + nc(17, "17", "0x11", "0o21", "0b10001"), + nc(31, "31", "0x1f", "0o37", "0b11111"), + nc(32, "32", "0x20", "0o40", "0b100000"), + nc(33, "33", "0x21", "0o41", "0b100001"), + nc(63, "63", "0x3f", "0o77", "0b111111"), + nc(64, "64", "0x40", "0o100", "0b1000000"), + nc(65, "65", "0x41", "0o101", "0b1000001"), + nc(91, "91", "0x5b", "0o133", "0b1011011"), + nc(92, "92", "0x5c", "0o134", "0b1011100"), + nc(93, "93", "0x5d", "0o135", "0b1011101"), + nc(99, "99", "0x63", "0o143", "0b1100011"), + nc(100, "100", "0x64", "0o144", "0b1100100"), + nc(101, "101", "0x65", "0o145", "0b1100101"), + nc(127, "127", "0x7f", "0o177", "0b1111111"), + nc(128, "128", "0x80", "0o200", "0b10000000"), + nc(129, "129", "0x81", "0o201", "0b10000001"), + nc(255, "255", "0xff", "0o377", "0b11111111"), + nc(256, "256", "0x100", "0o400", "0b100000000"), + nc(257, "257", "0x101", "0o401", "0b100000001"), + nc(511, "511", "0x1ff", "0o777", "0b111111111"), + nc(512, "512", "0x200", "0o1000", "0b1000000000"), + nc(513, "513", "0x201", "0o1001", "0b1000000001"), + nc(921, "921", "0x399", "0o1631", "0b1110011001"), + nc(922, "922", "0x39a", "0o1632", "0b1110011010"), + nc(923, "923", "0x39b", "0o1633", "0b1110011011"), + nc(999, "999", "0x3e7", "0o1747", "0b1111100111"), + nc(1000, "1000", "0x3e8", "0o1750", "0b1111101000"), + nc(1001, "1001", "0x3e9", "0o1751", "0b1111101001"), + nc(1023, "1023", "0x3ff", "0o1777", "0b1111111111"), + nc(1024, "1024", "0x400", "0o2000", "0b10000000000"), + nc(1025, "1025", "0x401", "0o2001", "0b10000000001"), + nc(2047, "2047", "0x7ff", "0o3777", "0b11111111111"), + nc(2048, "2048", "0x800", "0o4000", "0b100000000000"), + nc(2049, "2049", "0x801", "0o4001", "0b100000000001"), + nc(4095, "4095", "0xfff", "0o7777", "0b111111111111"), + nc(4096, "4096", "0x1000", "0o10000", "0b1000000000000"), + nc(4097, "4097", "0x1001", "0o10001", "0b1000000000001"), + nc(8191, "8191", "0x1fff", "0o17777", "0b1111111111111"), + nc(8192, "8192", "0x2000", "0o20000", "0b10000000000000"), + nc(8193, "8193", "0x2001", "0o20001", "0b10000000000001"), + nc(9222, "9222", "0x2406", "0o22006", "0b10010000000110"), + nc(9223, "9223", "0x2407", "0o22007", "0b10010000000111"), + nc(9224, "9224", "0x2408", "0o22010", "0b10010000001000"), + nc(9999, "9999", "0x270f", "0o23417", "0b10011100001111"), + nc(10000, "10000", "0x2710", "0o23420", "0b10011100010000"), + nc(10001, "10001", "0x2711", "0o23421", "0b10011100010001"), + nc(16383, "16383", "0x3fff", "0o37777", "0b11111111111111"), + nc(16384, "16384", "0x4000", "0o40000", "0b100000000000000"), + nc(16385, "16385", "0x4001", "0o40001", "0b100000000000001"), + nc(32767, "32767", "0x7fff", "0o77777", "0b111111111111111"), + nc(32768, "32768", "0x8000", "0o100000", "0b1000000000000000"), + nc(32769, "32769", "0x8001", "0o100001", "0b1000000000000001"), + nc(65535, "65535", "0xffff", "0o177777", "0b1111111111111111"), + nc(65536, "65536", "0x10000", "0o200000", "0b10000000000000000"), + nc(65537, "65537", "0x10001", "0o200001", "0b10000000000000001"), + nc(92232, "92232", "0x16848", "0o264110", "0b10110100001001000"), + nc(92233, "92233", "0x16849", "0o264111", "0b10110100001001001"), + nc(92234, "92234", "0x1684a", "0o264112", "0b10110100001001010"), + nc(99999, "99999", "0x1869f", "0o303237", "0b11000011010011111"), + nc(100000, "100000", "0x186a0", "0o303240", "0b11000011010100000"), + nc(100001, "100001", "0x186a1", "0o303241", "0b11000011010100001"), + nc(131071, "131071", "0x1ffff", "0o377777", "0b11111111111111111"), + nc(131072, "131072", "0x20000", "0o400000", "0b100000000000000000"), + nc(131073, "131073", "0x20001", "0o400001", "0b100000000000000001"), + nc(262143, "262143", "0x3ffff", "0o777777", "0b111111111111111111"), + nc(262144, "262144", "0x40000", "0o1000000", "0b1000000000000000000"), + nc(262145, "262145", "0x40001", "0o1000001", "0b1000000000000000001"), + nc(524287, "524287", "0x7ffff", "0o1777777", "0b1111111111111111111"), + nc(524288, "524288", "0x80000", "0o2000000", "0b10000000000000000000"), + nc(524289, "524289", "0x80001", "0o2000001", "0b10000000000000000001"), + nc(922336, "922336", "0xe12e0", "0o3411340", "0b11100001001011100000"), + nc(922337, "922337", "0xe12e1", "0o3411341", "0b11100001001011100001"), + nc(922338, "922338", "0xe12e2", "0o3411342", "0b11100001001011100010"), + nc(999999, "999999", "0xf423f", "0o3641077", "0b11110100001000111111"), + nc(1000000, "1000000", "0xf4240", "0o3641100", "0b11110100001001000000"), + nc(1000001, "1000001", "0xf4241", "0o3641101", "0b11110100001001000001"), + nc(1048575, "1048575", "0xfffff", "0o3777777", "0b11111111111111111111"), + nc(1048576, "1048576", "0x100000", "0o4000000", "0b100000000000000000000"), + nc(1048577, "1048577", "0x100001", "0o4000001", "0b100000000000000000001"), + nc(2097151, "2097151", "0x1fffff", "0o7777777", "0b111111111111111111111"), + nc(2097152, "2097152", "0x200000", "0o10000000", "0b1000000000000000000000"), + nc(2097153, "2097153", "0x200001", "0o10000001", "0b1000000000000000000001"), + nc(4194303, "4194303", "0x3fffff", "0o17777777", "0b1111111111111111111111"), + nc(4194304, "4194304", "0x400000", "0o20000000", "0b10000000000000000000000"), + nc(4194305, "4194305", "0x400001", "0o20000001", "0b10000000000000000000001"), + nc(8388607, "8388607", "0x7fffff", "0o37777777", "0b11111111111111111111111"), + nc(8388608, "8388608", "0x800000", "0o40000000", "0b100000000000000000000000"), + nc(8388609, "8388609", "0x800001", "0o40000001", "0b100000000000000000000001"), + nc(9223371, "9223371", "0x8cbccb", "0o43136313", "0b100011001011110011001011"), + nc(9223372, "9223372", "0x8cbccc", "0o43136314", "0b100011001011110011001100"), + nc(9223373, "9223373", "0x8cbccd", "0o43136315", "0b100011001011110011001101"), + nc(9999999, "9999999", "0x98967f", "0o46113177", "0b100110001001011001111111"), + nc(10000000, "10000000", "0x989680", "0o46113200", "0b100110001001011010000000"), + nc(10000001, "10000001", "0x989681", "0o46113201", "0b100110001001011010000001"), + nc(16777215, "16777215", "0xffffff", "0o77777777", "0b111111111111111111111111"), + nc(16777216, "16777216", "0x1000000", "0o100000000", "0b1000000000000000000000000"), + nc(16777217, "16777217", "0x1000001", "0o100000001", "0b1000000000000000000000001"), + nc(33554431, "33554431", "0x1ffffff", "0o177777777", "0b1111111111111111111111111"), + nc(33554432, "33554432", "0x2000000", "0o200000000", "0b10000000000000000000000000"), + nc(33554433, "33554433", "0x2000001", "0o200000001", "0b10000000000000000000000001"), + nc(67108863, "67108863", "0x3ffffff", "0o377777777", "0b11111111111111111111111111"), + nc(67108864, "67108864", "0x4000000", "0o400000000", "0b100000000000000000000000000"), + nc(67108865, "67108865", "0x4000001", "0o400000001", "0b100000000000000000000000001"), + nc(92233719, "92233719", "0x57f5ff7", "0o537657767", "0b101011111110101111111110111"), + nc(92233720, "92233720", "0x57f5ff8", "0o537657770", "0b101011111110101111111111000"), + nc(92233721, "92233721", "0x57f5ff9", "0o537657771", "0b101011111110101111111111001"), + nc(99999999, "99999999", "0x5f5e0ff", "0o575360377", "0b101111101011110000011111111"), + nc(100000000, "100000000", "0x5f5e100", "0o575360400", "0b101111101011110000100000000"), + nc(100000001, "100000001", "0x5f5e101", "0o575360401", "0b101111101011110000100000001"), + nc(134217727, "134217727", "0x7ffffff", "0o777777777", "0b111111111111111111111111111"), + nc(134217728, "134217728", "0x8000000", "0o1000000000", "0b1000000000000000000000000000"), + nc(134217729, "134217729", "0x8000001", "0o1000000001", "0b1000000000000000000000000001"), + nc(268435455, "268435455", "0xfffffff", "0o1777777777", "0b1111111111111111111111111111"), + nc(268435456, "268435456", "0x10000000", "0o2000000000", "0b10000000000000000000000000000"), + nc(268435457, "268435457", "0x10000001", "0o2000000001", "0b10000000000000000000000000001"), + nc(536870911, "536870911", "0x1fffffff", "0o3777777777", "0b11111111111111111111111111111"), + nc(536870912, "536870912", "0x20000000", "0o4000000000", "0b100000000000000000000000000000"), + nc(536870913, "536870913", "0x20000001", "0o4000000001", "0b100000000000000000000000000001"), + nc(922337202, "922337202", "0x36f9bfb2", "0o6676337662", "0b110110111110011011111110110010"), + nc(922337203, "922337203", "0x36f9bfb3", "0o6676337663", "0b110110111110011011111110110011"), + nc(922337204, "922337204", "0x36f9bfb4", "0o6676337664", "0b110110111110011011111110110100"), + nc(999999999, "999999999", "0x3b9ac9ff", "0o7346544777", "0b111011100110101100100111111111"), + nc(1000000000, "1000000000", "0x3b9aca00", "0o7346545000", "0b111011100110101100101000000000"), + nc(1000000001, "1000000001", "0x3b9aca01", "0o7346545001", "0b111011100110101100101000000001"), + nc(1073741823, "1073741823", "0x3fffffff", "0o7777777777", "0b111111111111111111111111111111"), + nc(1073741824, "1073741824", "0x40000000", "0o10000000000", "0b1000000000000000000000000000000"), + nc(1073741825, "1073741825", "0x40000001", "0o10000000001", "0b1000000000000000000000000000001"), + nc(2147483647, "2147483647", "0x7fffffff", "0o17777777777", "0b1111111111111111111111111111111"), + nc(2147483648, "2147483648", "0x80000000", "0o20000000000", "0b10000000000000000000000000000000"), + nc(2147483649, "2147483649", "0x80000001", "0o20000000001", "0b10000000000000000000000000000001"), + nc(4294967295, "4294967295", "0xffffffff", "0o37777777777", "0b11111111111111111111111111111111"), + nc(4294967296, "4294967296", "0x100000000", "0o40000000000", "0b100000000000000000000000000000000"), + nc(4294967297, "4294967297", "0x100000001", "0o40000000001", "0b100000000000000000000000000000001"), + nc(8589934591, "8589934591", "0x1ffffffff", "0o77777777777", "0b111111111111111111111111111111111"), + nc(8589934592, "8589934592", "0x200000000", "0o100000000000", "0b1000000000000000000000000000000000"), + nc(8589934593, "8589934593", "0x200000001", "0o100000000001", "0b1000000000000000000000000000000001"), + nc(9223372035, "9223372035", "0x225c17d03", "0o104560276403", "0b1000100101110000010111110100000011"), + nc(9223372036, "9223372036", "0x225c17d04", "0o104560276404", "0b1000100101110000010111110100000100"), + nc(9223372037, "9223372037", "0x225c17d05", "0o104560276405", "0b1000100101110000010111110100000101"), + nc(9999999999, "9999999999", "0x2540be3ff", "0o112402761777", "0b1001010100000010111110001111111111"), + nc(10000000000, "10000000000", "0x2540be400", "0o112402762000", "0b1001010100000010111110010000000000"), + nc(10000000001, "10000000001", "0x2540be401", "0o112402762001", "0b1001010100000010111110010000000001"), + nc(17179869183, "17179869183", "0x3ffffffff", "0o177777777777", "0b1111111111111111111111111111111111"), + nc(17179869184, "17179869184", "0x400000000", "0o200000000000", "0b10000000000000000000000000000000000"), + nc(17179869185, "17179869185", "0x400000001", "0o200000000001", "0b10000000000000000000000000000000001"), + nc(34359738367, "34359738367", "0x7ffffffff", "0o377777777777", "0b11111111111111111111111111111111111"), + nc(34359738368, "34359738368", "0x800000000", "0o400000000000", "0b100000000000000000000000000000000000"), + nc(34359738369, "34359738369", "0x800000001", "0o400000000001", "0b100000000000000000000000000000000001"), + nc(68719476735, "68719476735", "0xfffffffff", "0o777777777777", "0b111111111111111111111111111111111111"), + nc(68719476736, "68719476736", "0x1000000000", "0o1000000000000", "0b1000000000000000000000000000000000000"), + nc(68719476737, "68719476737", "0x1000000001", "0o1000000000001", "0b1000000000000000000000000000000000001"), + nc(92233720367, "92233720367", "0x15798ee22f", "0o1257143561057", "0b1010101111001100011101110001000101111"), + nc(92233720368, "92233720368", "0x15798ee230", "0o1257143561060", "0b1010101111001100011101110001000110000"), + nc(92233720369, "92233720369", "0x15798ee231", "0o1257143561061", "0b1010101111001100011101110001000110001"), + nc(99999999999, "99999999999", "0x174876e7ff", "0o1351035563777", "0b1011101001000011101101110011111111111"), + nc(100000000000, "100000000000", "0x174876e800", "0o1351035564000", "0b1011101001000011101101110100000000000"), + nc(100000000001, "100000000001", "0x174876e801", "0o1351035564001", "0b1011101001000011101101110100000000001"), + nc(137438953471, "137438953471", "0x1fffffffff", "0o1777777777777", "0b1111111111111111111111111111111111111"), + nc(137438953472, "137438953472", "0x2000000000", "0o2000000000000", "0b10000000000000000000000000000000000000"), + nc(137438953473, "137438953473", "0x2000000001", "0o2000000000001", "0b10000000000000000000000000000000000001"), + nc(274877906943, "274877906943", "0x3fffffffff", "0o3777777777777", "0b11111111111111111111111111111111111111"), + nc(274877906944, "274877906944", "0x4000000000", "0o4000000000000", "0b100000000000000000000000000000000000000"), + nc(274877906945, "274877906945", "0x4000000001", "0o4000000000001", "0b100000000000000000000000000000000000001"), + nc(549755813887, "549755813887", "0x7fffffffff", "0o7777777777777", "0b111111111111111111111111111111111111111"), + nc(549755813888, "549755813888", "0x8000000000", "0o10000000000000", "0b1000000000000000000000000000000000000000"), + nc(549755813889, "549755813889", "0x8000000001", "0o10000000000001", "0b1000000000000000000000000000000000000001"), + nc(922337203684, "922337203684", "0xd6bf94d5e4", "0o15327745152744", "0b1101011010111111100101001101010111100100"), + nc(922337203685, "922337203685", "0xd6bf94d5e5", "0o15327745152745", "0b1101011010111111100101001101010111100101"), + nc(922337203686, "922337203686", "0xd6bf94d5e6", "0o15327745152746", "0b1101011010111111100101001101010111100110"), + nc(999999999999, "999999999999", "0xe8d4a50fff", "0o16432451207777", "0b1110100011010100101001010000111111111111"), + nc(1000000000000, "1000000000000", "0xe8d4a51000", "0o16432451210000", "0b1110100011010100101001010001000000000000"), + nc(1000000000001, "1000000000001", "0xe8d4a51001", "0o16432451210001", "0b1110100011010100101001010001000000000001"), + nc(1099511627775, "1099511627775", "0xffffffffff", "0o17777777777777", "0b1111111111111111111111111111111111111111"), + nc(1099511627776, "1099511627776", "0x10000000000", "0o20000000000000", "0b10000000000000000000000000000000000000000"), + nc(1099511627777, "1099511627777", "0x10000000001", "0o20000000000001", "0b10000000000000000000000000000000000000001"), + nc(2199023255551, "2199023255551", "0x1ffffffffff", "0o37777777777777", "0b11111111111111111111111111111111111111111"), + nc(2199023255552, "2199023255552", "0x20000000000", "0o40000000000000", "0b100000000000000000000000000000000000000000"), + nc(2199023255553, "2199023255553", "0x20000000001", "0o40000000000001", "0b100000000000000000000000000000000000000001"), + nc(4398046511103, "4398046511103", "0x3ffffffffff", "0o77777777777777", "0b111111111111111111111111111111111111111111"), + nc(4398046511104, "4398046511104", "0x40000000000", "0o100000000000000", "0b1000000000000000000000000000000000000000000"), + nc(4398046511105, "4398046511105", "0x40000000001", "0o100000000000001", "0b1000000000000000000000000000000000000000001"), + nc(8796093022207, "8796093022207", "0x7ffffffffff", "0o177777777777777", "0b1111111111111111111111111111111111111111111"), + nc(8796093022208, "8796093022208", "0x80000000000", "0o200000000000000", "0b10000000000000000000000000000000000000000000"), + nc(8796093022209, "8796093022209", "0x80000000001", "0o200000000000001", "0b10000000000000000000000000000000000000000001"), + nc(9223372036853, "9223372036853", "0x8637bd05af5", "0o206157364055365", "0b10000110001101111011110100000101101011110101"), + nc(9223372036854, "9223372036854", "0x8637bd05af6", "0o206157364055366", "0b10000110001101111011110100000101101011110110"), + nc(9223372036855, "9223372036855", "0x8637bd05af7", "0o206157364055367", "0b10000110001101111011110100000101101011110111"), + nc(9999999999999, "9999999999999", "0x9184e729fff", "0o221411634517777", "0b10010001100001001110011100101001111111111111"), + nc(10000000000000, "10000000000000", "0x9184e72a000", "0o221411634520000", "0b10010001100001001110011100101010000000000000"), + nc(10000000000001, "10000000000001", "0x9184e72a001", "0o221411634520001", "0b10010001100001001110011100101010000000000001"), + nc(17592186044415, "17592186044415", "0xfffffffffff", "0o377777777777777", "0b11111111111111111111111111111111111111111111"), + nc(17592186044416, "17592186044416", "0x100000000000", "0o400000000000000", "0b100000000000000000000000000000000000000000000"), + nc(17592186044417, "17592186044417", "0x100000000001", "0o400000000000001", "0b100000000000000000000000000000000000000000001"), + nc(35184372088831, "35184372088831", "0x1fffffffffff", "0o777777777777777", "0b111111111111111111111111111111111111111111111"), + nc(35184372088832, "35184372088832", "0x200000000000", "0o1000000000000000", "0b1000000000000000000000000000000000000000000000"), + nc(35184372088833, "35184372088833", "0x200000000001", "0o1000000000000001", "0b1000000000000000000000000000000000000000000001"), + nc(70368744177663, "70368744177663", "0x3fffffffffff", "0o1777777777777777", "0b1111111111111111111111111111111111111111111111"), + nc(70368744177664, "70368744177664", "0x400000000000", "0o2000000000000000", "0b10000000000000000000000000000000000000000000000"), + nc(70368744177665, "70368744177665", "0x400000000001", "0o2000000000000001", "0b10000000000000000000000000000000000000000000001"), + nc(92233720368546, "92233720368546", "0x53e2d6238da2", "0o2476132610706642", "0b10100111110001011010110001000111000110110100010"), + nc(92233720368547, "92233720368547", "0x53e2d6238da3", "0o2476132610706643", "0b10100111110001011010110001000111000110110100011"), + nc(92233720368548, "92233720368548", "0x53e2d6238da4", "0o2476132610706644", "0b10100111110001011010110001000111000110110100100"), + nc(99999999999999, "99999999999999", "0x5af3107a3fff", "0o2657142036437777", "0b10110101111001100010000011110100011111111111111"), + nc(100000000000000, "100000000000000", "0x5af3107a4000", "0o2657142036440000", "0b10110101111001100010000011110100100000000000000"), + nc(100000000000001, "100000000000001", "0x5af3107a4001", "0o2657142036440001", "0b10110101111001100010000011110100100000000000001"), + nc(140737488355327, "140737488355327", "0x7fffffffffff", "0o3777777777777777", "0b11111111111111111111111111111111111111111111111"), + nc(140737488355328, "140737488355328", "0x800000000000", "0o4000000000000000", "0b100000000000000000000000000000000000000000000000"), + nc(140737488355329, "140737488355329", "0x800000000001", "0o4000000000000001", "0b100000000000000000000000000000000000000000000001"), + nc(281474976710655, "281474976710655", "0xffffffffffff", "0o7777777777777777", "0b111111111111111111111111111111111111111111111111"), + nc(281474976710656, "281474976710656", "0x1000000000000", "0o10000000000000000", "0b1000000000000000000000000000000000000000000000000"), + nc(281474976710657, "281474976710657", "0x1000000000001", "0o10000000000000001", "0b1000000000000000000000000000000000000000000000001"), + nc(562949953421311, "562949953421311", "0x1ffffffffffff", "0o17777777777777777", "0b1111111111111111111111111111111111111111111111111"), + nc(562949953421312, "562949953421312", "0x2000000000000", "0o20000000000000000", "0b10000000000000000000000000000000000000000000000000"), + nc(562949953421313, "562949953421313", "0x2000000000001", "0o20000000000000001", "0b10000000000000000000000000000000000000000000000001"), + nc(922337203685476, "922337203685476", "0x346dc5d638864", "0o32155613530704144", "0b11010001101101110001011101011000111000100001100100"), + nc(922337203685477, "922337203685477", "0x346dc5d638865", "0o32155613530704145", "0b11010001101101110001011101011000111000100001100101"), + nc(922337203685478, "922337203685478", "0x346dc5d638866", "0o32155613530704146", "0b11010001101101110001011101011000111000100001100110"), + nc(999999999999999, "999999999999999", "0x38d7ea4c67fff", "0o34327724461477777", "0b11100011010111111010100100110001100111111111111111"), + nc(1000000000000000, "1000000000000000", "0x38d7ea4c68000", "0o34327724461500000", "0b11100011010111111010100100110001101000000000000000"), + nc(1000000000000001, "1000000000000001", "0x38d7ea4c68001", "0o34327724461500001", "0b11100011010111111010100100110001101000000000000001"), + nc(1125899906842623, "1125899906842623", "0x3ffffffffffff", "0o37777777777777777", "0b11111111111111111111111111111111111111111111111111"), + nc(1125899906842624, "1125899906842624", "0x4000000000000", "0o40000000000000000", "0b100000000000000000000000000000000000000000000000000"), + nc(1125899906842625, "1125899906842625", "0x4000000000001", "0o40000000000000001", "0b100000000000000000000000000000000000000000000000001"), + nc(2251799813685247, "2251799813685247", "0x7ffffffffffff", "0o77777777777777777", "0b111111111111111111111111111111111111111111111111111"), + nc(2251799813685248, "2251799813685248", "0x8000000000000", "0o100000000000000000", "0b1000000000000000000000000000000000000000000000000000"), + nc(2251799813685249, "2251799813685249", "0x8000000000001", "0o100000000000000001", "0b1000000000000000000000000000000000000000000000000001"), + nc(4503599627370495, "4503599627370495", "0xfffffffffffff", "0o177777777777777777", "0b1111111111111111111111111111111111111111111111111111"), + nc(4503599627370496, "4503599627370496", "0x10000000000000", "0o200000000000000000", "0b10000000000000000000000000000000000000000000000000000"), + nc(4503599627370497, "4503599627370497", "0x10000000000001", "0o200000000000000001", "0b10000000000000000000000000000000000000000000000000001"), + nc(9007199254740991, "9007199254740991", "0x1fffffffffffff", "0o377777777777777777", "0b11111111111111111111111111111111111111111111111111111"), + nc(9007199254740992, "9007199254740992", "0x20000000000000", "0o400000000000000000", "0b100000000000000000000000000000000000000000000000000000"), + nc(9007199254740993, "9007199254740993", "0x20000000000001", "0o400000000000000001", "0b100000000000000000000000000000000000000000000000000001"), + nc(9223372036854775, "9223372036854775", "0x20c49ba5e353f7", "0o406111564570651767", "0b100000110001001001101110100101111000110101001111110111"), + nc(9223372036854776, "9223372036854776", "0x20c49ba5e353f8", "0o406111564570651770", "0b100000110001001001101110100101111000110101001111111000"), + nc(9223372036854777, "9223372036854777", "0x20c49ba5e353f9", "0o406111564570651771", "0b100000110001001001101110100101111000110101001111111001"), + nc(9999999999999999, "9999999999999999", "0x2386f26fc0ffff", "0o434157115760177777", "0b100011100001101111001001101111110000001111111111111111"), + nc(10000000000000000, "10000000000000000", "0x2386f26fc10000", "0o434157115760200000", "0b100011100001101111001001101111110000010000000000000000"), + nc(10000000000000001, "10000000000000001", "0x2386f26fc10001", "0o434157115760200001", "0b100011100001101111001001101111110000010000000000000001"), + nc(18014398509481983, "18014398509481983", "0x3fffffffffffff", "0o777777777777777777", "0b111111111111111111111111111111111111111111111111111111"), + nc(18014398509481984, "18014398509481984", "0x40000000000000", "0o1000000000000000000", "0b1000000000000000000000000000000000000000000000000000000"), + nc(18014398509481985, "18014398509481985", "0x40000000000001", "0o1000000000000000001", "0b1000000000000000000000000000000000000000000000000000001"), + nc(36028797018963967, "36028797018963967", "0x7fffffffffffff", "0o1777777777777777777", "0b1111111111111111111111111111111111111111111111111111111"), + nc(36028797018963968, "36028797018963968", "0x80000000000000", "0o2000000000000000000", "0b10000000000000000000000000000000000000000000000000000000"), + nc(36028797018963969, "36028797018963969", "0x80000000000001", "0o2000000000000000001", "0b10000000000000000000000000000000000000000000000000000001"), + nc(72057594037927935, "72057594037927935", "0xffffffffffffff", "0o3777777777777777777", "0b11111111111111111111111111111111111111111111111111111111"), + nc(72057594037927936, "72057594037927936", "0x100000000000000", "0o4000000000000000000", "0b100000000000000000000000000000000000000000000000000000000"), + nc(72057594037927937, "72057594037927937", "0x100000000000001", "0o4000000000000000001", "0b100000000000000000000000000000000000000000000000000000001"), + nc(92233720368547759, "92233720368547759", "0x147ae147ae147af", "0o5075341217270243657", "0b101000111101011100001010001111010111000010100011110101111"), + nc(92233720368547760, "92233720368547760", "0x147ae147ae147b0", "0o5075341217270243660", "0b101000111101011100001010001111010111000010100011110110000"), + nc(92233720368547761, "92233720368547761", "0x147ae147ae147b1", "0o5075341217270243661", "0b101000111101011100001010001111010111000010100011110110001"), + nc(99999999999999999, "99999999999999999", "0x16345785d89ffff", "0o5432127413542377777", "0b101100011010001010111100001011101100010011111111111111111"), + nc(100000000000000000, "100000000000000000", "0x16345785d8a0000", "0o5432127413542400000", "0b101100011010001010111100001011101100010100000000000000000"), + nc(100000000000000001, "100000000000000001", "0x16345785d8a0001", "0o5432127413542400001", "0b101100011010001010111100001011101100010100000000000000001"), + nc(144115188075855871, "144115188075855871", "0x1ffffffffffffff", "0o7777777777777777777", "0b111111111111111111111111111111111111111111111111111111111"), + nc(144115188075855872, "144115188075855872", "0x200000000000000", "0o10000000000000000000", "0b1000000000000000000000000000000000000000000000000000000000"), + nc(144115188075855873, "144115188075855873", "0x200000000000001", "0o10000000000000000001", "0b1000000000000000000000000000000000000000000000000000000001"), + nc(288230376151711743, "288230376151711743", "0x3ffffffffffffff", "0o17777777777777777777", "0b1111111111111111111111111111111111111111111111111111111111"), + nc(288230376151711744, "288230376151711744", "0x400000000000000", "0o20000000000000000000", "0b10000000000000000000000000000000000000000000000000000000000"), + nc(288230376151711745, "288230376151711745", "0x400000000000001", "0o20000000000000000001", "0b10000000000000000000000000000000000000000000000000000000001"), + nc(576460752303423487, "576460752303423487", "0x7ffffffffffffff", "0o37777777777777777777", "0b11111111111111111111111111111111111111111111111111111111111"), + nc(576460752303423488, "576460752303423488", "0x800000000000000", "0o40000000000000000000", "0b100000000000000000000000000000000000000000000000000000000000"), + nc(576460752303423489, "576460752303423489", "0x800000000000001", "0o40000000000000000001", "0b100000000000000000000000000000000000000000000000000000000001"), + nc(922337203685477631, "922337203685477631", "0xcccccccccccccff", "0o63146314631463146377", "0b110011001100110011001100110011001100110011001100110011111111"), + nc(922337203685477632, "922337203685477632", "0xccccccccccccd00", "0o63146314631463146400", "0b110011001100110011001100110011001100110011001100110100000000"), + nc(922337203685477633, "922337203685477633", "0xccccccccccccd01", "0o63146314631463146401", "0b110011001100110011001100110011001100110011001100110100000001"), + nc(999999999999999999, "999999999999999999", "0xde0b6b3a763ffff", "0o67405553164730777777", "0b110111100000101101101011001110100111011000111111111111111111"), + nc(1000000000000000000, "1000000000000000000", "0xde0b6b3a7640000", "0o67405553164731000000", "0b110111100000101101101011001110100111011001000000000000000000"), + nc(1000000000000000001, "1000000000000000001", "0xde0b6b3a7640001", "0o67405553164731000001", "0b110111100000101101101011001110100111011001000000000000000001"), + nc(1152921504606846975, "1152921504606846975", "0xfffffffffffffff", "0o77777777777777777777", "0b111111111111111111111111111111111111111111111111111111111111"), + nc(1152921504606846976, "1152921504606846976", "0x1000000000000000", "0o100000000000000000000", "0b1000000000000000000000000000000000000000000000000000000000000"), + nc(1152921504606846977, "1152921504606846977", "0x1000000000000001", "0o100000000000000000001", "0b1000000000000000000000000000000000000000000000000000000000001"), + nc(2305843009213693951, "2305843009213693951", "0x1fffffffffffffff", "0o177777777777777777777", "0b1111111111111111111111111111111111111111111111111111111111111"), + nc(2305843009213693952, "2305843009213693952", "0x2000000000000000", "0o200000000000000000000", "0b10000000000000000000000000000000000000000000000000000000000000"), + nc(2305843009213693953, "2305843009213693953", "0x2000000000000001", "0o200000000000000000001", "0b10000000000000000000000000000000000000000000000000000000000001"), + nc(4611686018427387903, "4611686018427387903", "0x3fffffffffffffff", "0o377777777777777777777", "0b11111111111111111111111111111111111111111111111111111111111111"), + nc(4611686018427387904, "4611686018427387904", "0x4000000000000000", "0o400000000000000000000", "0b100000000000000000000000000000000000000000000000000000000000000"), + nc(4611686018427387905, "4611686018427387905", "0x4000000000000001", "0o400000000000000000001", "0b100000000000000000000000000000000000000000000000000000000000001"), + nc(9223372036854775802, "9223372036854775802", "0x7ffffffffffffffa", "0o777777777777777777772", "0b111111111111111111111111111111111111111111111111111111111111010"), + nc(9223372036854775803, "9223372036854775803", "0x7ffffffffffffffb", "0o777777777777777777773", "0b111111111111111111111111111111111111111111111111111111111111011"), + nc(9223372036854775804, "9223372036854775804", "0x7ffffffffffffffc", "0o777777777777777777774", "0b111111111111111111111111111111111111111111111111111111111111100"), + nc(9223372036854775805, "9223372036854775805", "0x7ffffffffffffffd", "0o777777777777777777775", "0b111111111111111111111111111111111111111111111111111111111111101"), + nc(9223372036854775806, "9223372036854775806", "0x7ffffffffffffffe", "0o777777777777777777776", "0b111111111111111111111111111111111111111111111111111111111111110"), + nc(9223372036854775807, "9223372036854775807", "0x7fffffffffffffff", "0o777777777777777777777", "0b111111111111111111111111111111111111111111111111111111111111111"), +#undef nc + }; +}; + + +template<> +struct numbers<uint64_t> +{ + using value_type = uint64_t; + static C4_INLINE_CONSTEXPR const number_case<uint64_t> vals[] = { +#define nc(val, dec, hex, bin, oct) \ + number_case<value_type>{UINT64_C(val), csubstr{dec}, csubstr{hex}, csubstr{bin}, csubstr{oct}} + nc(0, "0", "0x0", "0o0", "0b0"), + nc(1, "1", "0x1", "0o1", "0b1"), + nc(2, "2", "0x2", "0o2", "0b10"), + nc(3, "3", "0x3", "0o3", "0b11"), + nc(4, "4", "0x4", "0o4", "0b100"), + nc(5, "5", "0x5", "0o5", "0b101"), + nc(6, "6", "0x6", "0o6", "0b110"), + nc(7, "7", "0x7", "0o7", "0b111"), + nc(8, "8", "0x8", "0o10", "0b1000"), + nc(9, "9", "0x9", "0o11", "0b1001"), + nc(10, "10", "0xa", "0o12", "0b1010"), + nc(11, "11", "0xb", "0o13", "0b1011"), + nc(15, "15", "0xf", "0o17", "0b1111"), + nc(16, "16", "0x10", "0o20", "0b10000"), + nc(17, "17", "0x11", "0o21", "0b10001"), + nc(18, "18", "0x12", "0o22", "0b10010"), + nc(19, "19", "0x13", "0o23", "0b10011"), + nc(31, "31", "0x1f", "0o37", "0b11111"), + nc(32, "32", "0x20", "0o40", "0b100000"), + nc(33, "33", "0x21", "0o41", "0b100001"), + nc(63, "63", "0x3f", "0o77", "0b111111"), + nc(64, "64", "0x40", "0o100", "0b1000000"), + nc(65, "65", "0x41", "0o101", "0b1000001"), + nc(99, "99", "0x63", "0o143", "0b1100011"), + nc(100, "100", "0x64", "0o144", "0b1100100"), + nc(101, "101", "0x65", "0o145", "0b1100101"), + nc(127, "127", "0x7f", "0o177", "0b1111111"), + nc(128, "128", "0x80", "0o200", "0b10000000"), + nc(129, "129", "0x81", "0o201", "0b10000001"), + nc(183, "183", "0xb7", "0o267", "0b10110111"), + nc(184, "184", "0xb8", "0o270", "0b10111000"), + nc(185, "185", "0xb9", "0o271", "0b10111001"), + nc(255, "255", "0xff", "0o377", "0b11111111"), + nc(256, "256", "0x100", "0o400", "0b100000000"), + nc(257, "257", "0x101", "0o401", "0b100000001"), + nc(511, "511", "0x1ff", "0o777", "0b111111111"), + nc(512, "512", "0x200", "0o1000", "0b1000000000"), + nc(513, "513", "0x201", "0o1001", "0b1000000001"), + nc(999, "999", "0x3e7", "0o1747", "0b1111100111"), + nc(1000, "1000", "0x3e8", "0o1750", "0b1111101000"), + nc(1001, "1001", "0x3e9", "0o1751", "0b1111101001"), + nc(1023, "1023", "0x3ff", "0o1777", "0b1111111111"), + nc(1024, "1024", "0x400", "0o2000", "0b10000000000"), + nc(1025, "1025", "0x401", "0o2001", "0b10000000001"), + nc(1843, "1843", "0x733", "0o3463", "0b11100110011"), + nc(1844, "1844", "0x734", "0o3464", "0b11100110100"), + nc(1845, "1845", "0x735", "0o3465", "0b11100110101"), + nc(2047, "2047", "0x7ff", "0o3777", "0b11111111111"), + nc(2048, "2048", "0x800", "0o4000", "0b100000000000"), + nc(2049, "2049", "0x801", "0o4001", "0b100000000001"), + nc(4095, "4095", "0xfff", "0o7777", "0b111111111111"), + nc(4096, "4096", "0x1000", "0o10000", "0b1000000000000"), + nc(4097, "4097", "0x1001", "0o10001", "0b1000000000001"), + nc(8191, "8191", "0x1fff", "0o17777", "0b1111111111111"), + nc(8192, "8192", "0x2000", "0o20000", "0b10000000000000"), + nc(8193, "8193", "0x2001", "0o20001", "0b10000000000001"), + nc(9999, "9999", "0x270f", "0o23417", "0b10011100001111"), + nc(10000, "10000", "0x2710", "0o23420", "0b10011100010000"), + nc(10001, "10001", "0x2711", "0o23421", "0b10011100010001"), + nc(16383, "16383", "0x3fff", "0o37777", "0b11111111111111"), + nc(16384, "16384", "0x4000", "0o40000", "0b100000000000000"), + nc(16385, "16385", "0x4001", "0o40001", "0b100000000000001"), + nc(18445, "18445", "0x480d", "0o44015", "0b100100000001101"), + nc(18446, "18446", "0x480e", "0o44016", "0b100100000001110"), + nc(18447, "18447", "0x480f", "0o44017", "0b100100000001111"), + nc(32767, "32767", "0x7fff", "0o77777", "0b111111111111111"), + nc(32768, "32768", "0x8000", "0o100000", "0b1000000000000000"), + nc(32769, "32769", "0x8001", "0o100001", "0b1000000000000001"), + nc(65535, "65535", "0xffff", "0o177777", "0b1111111111111111"), + nc(65536, "65536", "0x10000", "0o200000", "0b10000000000000000"), + nc(65537, "65537", "0x10001", "0o200001", "0b10000000000000001"), + nc(99999, "99999", "0x1869f", "0o303237", "0b11000011010011111"), + nc(100000, "100000", "0x186a0", "0o303240", "0b11000011010100000"), + nc(100001, "100001", "0x186a1", "0o303241", "0b11000011010100001"), + nc(131071, "131071", "0x1ffff", "0o377777", "0b11111111111111111"), + nc(131072, "131072", "0x20000", "0o400000", "0b100000000000000000"), + nc(131073, "131073", "0x20001", "0o400001", "0b100000000000000001"), + nc(184466, "184466", "0x2d092", "0o550222", "0b101101000010010010"), + nc(184467, "184467", "0x2d093", "0o550223", "0b101101000010010011"), + nc(184468, "184468", "0x2d094", "0o550224", "0b101101000010010100"), + nc(262143, "262143", "0x3ffff", "0o777777", "0b111111111111111111"), + nc(262144, "262144", "0x40000", "0o1000000", "0b1000000000000000000"), + nc(262145, "262145", "0x40001", "0o1000001", "0b1000000000000000001"), + nc(524287, "524287", "0x7ffff", "0o1777777", "0b1111111111111111111"), + nc(524288, "524288", "0x80000", "0o2000000", "0b10000000000000000000"), + nc(524289, "524289", "0x80001", "0o2000001", "0b10000000000000000001"), + nc(999999, "999999", "0xf423f", "0o3641077", "0b11110100001000111111"), + nc(1000000, "1000000", "0xf4240", "0o3641100", "0b11110100001001000000"), + nc(1000001, "1000001", "0xf4241", "0o3641101", "0b11110100001001000001"), + nc(1048575, "1048575", "0xfffff", "0o3777777", "0b11111111111111111111"), + nc(1048576, "1048576", "0x100000", "0o4000000", "0b100000000000000000000"), + nc(1048577, "1048577", "0x100001", "0o4000001", "0b100000000000000000001"), + nc(1844673, "1844673", "0x1c25c1", "0o7022701", "0b111000010010111000001"), + nc(1844674, "1844674", "0x1c25c2", "0o7022702", "0b111000010010111000010"), + nc(1844675, "1844675", "0x1c25c3", "0o7022703", "0b111000010010111000011"), + nc(2097151, "2097151", "0x1fffff", "0o7777777", "0b111111111111111111111"), + nc(2097152, "2097152", "0x200000", "0o10000000", "0b1000000000000000000000"), + nc(2097153, "2097153", "0x200001", "0o10000001", "0b1000000000000000000001"), + nc(4194303, "4194303", "0x3fffff", "0o17777777", "0b1111111111111111111111"), + nc(4194304, "4194304", "0x400000", "0o20000000", "0b10000000000000000000000"), + nc(4194305, "4194305", "0x400001", "0o20000001", "0b10000000000000000000001"), + nc(8388607, "8388607", "0x7fffff", "0o37777777", "0b11111111111111111111111"), + nc(8388608, "8388608", "0x800000", "0o40000000", "0b100000000000000000000000"), + nc(8388609, "8388609", "0x800001", "0o40000001", "0b100000000000000000000001"), + nc(9999999, "9999999", "0x98967f", "0o46113177", "0b100110001001011001111111"), + nc(10000000, "10000000", "0x989680", "0o46113200", "0b100110001001011010000000"), + nc(10000001, "10000001", "0x989681", "0o46113201", "0b100110001001011010000001"), + nc(16777215, "16777215", "0xffffff", "0o77777777", "0b111111111111111111111111"), + nc(16777216, "16777216", "0x1000000", "0o100000000", "0b1000000000000000000000000"), + nc(16777217, "16777217", "0x1000001", "0o100000001", "0b1000000000000000000000001"), + nc(18446743, "18446743", "0x1197997", "0o106274627", "0b1000110010111100110010111"), + nc(18446744, "18446744", "0x1197998", "0o106274630", "0b1000110010111100110011000"), + nc(18446745, "18446745", "0x1197999", "0o106274631", "0b1000110010111100110011001"), + nc(33554431, "33554431", "0x1ffffff", "0o177777777", "0b1111111111111111111111111"), + nc(33554432, "33554432", "0x2000000", "0o200000000", "0b10000000000000000000000000"), + nc(33554433, "33554433", "0x2000001", "0o200000001", "0b10000000000000000000000001"), + nc(67108863, "67108863", "0x3ffffff", "0o377777777", "0b11111111111111111111111111"), + nc(67108864, "67108864", "0x4000000", "0o400000000", "0b100000000000000000000000000"), + nc(67108865, "67108865", "0x4000001", "0o400000001", "0b100000000000000000000000001"), + nc(99999999, "99999999", "0x5f5e0ff", "0o575360377", "0b101111101011110000011111111"), + nc(100000000, "100000000", "0x5f5e100", "0o575360400", "0b101111101011110000100000000"), + nc(100000001, "100000001", "0x5f5e101", "0o575360401", "0b101111101011110000100000001"), + nc(134217727, "134217727", "0x7ffffff", "0o777777777", "0b111111111111111111111111111"), + nc(134217728, "134217728", "0x8000000", "0o1000000000", "0b1000000000000000000000000000"), + nc(134217729, "134217729", "0x8000001", "0o1000000001", "0b1000000000000000000000000001"), + nc(184467439, "184467439", "0xafebfef", "0o1277537757", "0b1010111111101011111111101111"), + nc(184467440, "184467440", "0xafebff0", "0o1277537760", "0b1010111111101011111111110000"), + nc(184467441, "184467441", "0xafebff1", "0o1277537761", "0b1010111111101011111111110001"), + nc(268435455, "268435455", "0xfffffff", "0o1777777777", "0b1111111111111111111111111111"), + nc(268435456, "268435456", "0x10000000", "0o2000000000", "0b10000000000000000000000000000"), + nc(268435457, "268435457", "0x10000001", "0o2000000001", "0b10000000000000000000000000001"), + nc(536870911, "536870911", "0x1fffffff", "0o3777777777", "0b11111111111111111111111111111"), + nc(536870912, "536870912", "0x20000000", "0o4000000000", "0b100000000000000000000000000000"), + nc(536870913, "536870913", "0x20000001", "0o4000000001", "0b100000000000000000000000000001"), + nc(999999999, "999999999", "0x3b9ac9ff", "0o7346544777", "0b111011100110101100100111111111"), + nc(1000000000, "1000000000", "0x3b9aca00", "0o7346545000", "0b111011100110101100101000000000"), + nc(1000000001, "1000000001", "0x3b9aca01", "0o7346545001", "0b111011100110101100101000000001"), + nc(1073741823, "1073741823", "0x3fffffff", "0o7777777777", "0b111111111111111111111111111111"), + nc(1073741824, "1073741824", "0x40000000", "0o10000000000", "0b1000000000000000000000000000000"), + nc(1073741825, "1073741825", "0x40000001", "0o10000000001", "0b1000000000000000000000000000001"), + nc(1844674406, "1844674406", "0x6df37f66", "0o15574677546", "0b1101101111100110111111101100110"), + nc(1844674407, "1844674407", "0x6df37f67", "0o15574677547", "0b1101101111100110111111101100111"), + nc(1844674408, "1844674408", "0x6df37f68", "0o15574677550", "0b1101101111100110111111101101000"), + nc(2147483647, "2147483647", "0x7fffffff", "0o17777777777", "0b1111111111111111111111111111111"), + nc(2147483648, "2147483648", "0x80000000", "0o20000000000", "0b10000000000000000000000000000000"), + nc(2147483649, "2147483649", "0x80000001", "0o20000000001", "0b10000000000000000000000000000001"), + nc(4294967295, "4294967295", "0xffffffff", "0o37777777777", "0b11111111111111111111111111111111"), + nc(4294967296, "4294967296", "0x100000000", "0o40000000000", "0b100000000000000000000000000000000"), + nc(4294967297, "4294967297", "0x100000001", "0o40000000001", "0b100000000000000000000000000000001"), + nc(8589934591, "8589934591", "0x1ffffffff", "0o77777777777", "0b111111111111111111111111111111111"), + nc(8589934592, "8589934592", "0x200000000", "0o100000000000", "0b1000000000000000000000000000000000"), + nc(8589934593, "8589934593", "0x200000001", "0o100000000001", "0b1000000000000000000000000000000001"), + nc(9999999999, "9999999999", "0x2540be3ff", "0o112402761777", "0b1001010100000010111110001111111111"), + nc(10000000000, "10000000000", "0x2540be400", "0o112402762000", "0b1001010100000010111110010000000000"), + nc(10000000001, "10000000001", "0x2540be401", "0o112402762001", "0b1001010100000010111110010000000001"), + nc(17179869183, "17179869183", "0x3ffffffff", "0o177777777777", "0b1111111111111111111111111111111111"), + nc(17179869184, "17179869184", "0x400000000", "0o200000000000", "0b10000000000000000000000000000000000"), + nc(17179869185, "17179869185", "0x400000001", "0o200000000001", "0b10000000000000000000000000000000001"), + nc(18446744072, "18446744072", "0x44b82fa08", "0o211340575010", "0b10001001011100000101111101000001000"), + nc(18446744073, "18446744073", "0x44b82fa09", "0o211340575011", "0b10001001011100000101111101000001001"), + nc(18446744074, "18446744074", "0x44b82fa0a", "0o211340575012", "0b10001001011100000101111101000001010"), + nc(34359738367, "34359738367", "0x7ffffffff", "0o377777777777", "0b11111111111111111111111111111111111"), + nc(34359738368, "34359738368", "0x800000000", "0o400000000000", "0b100000000000000000000000000000000000"), + nc(34359738369, "34359738369", "0x800000001", "0o400000000001", "0b100000000000000000000000000000000001"), + nc(68719476735, "68719476735", "0xfffffffff", "0o777777777777", "0b111111111111111111111111111111111111"), + nc(68719476736, "68719476736", "0x1000000000", "0o1000000000000", "0b1000000000000000000000000000000000000"), + nc(68719476737, "68719476737", "0x1000000001", "0o1000000000001", "0b1000000000000000000000000000000000001"), + nc(99999999999, "99999999999", "0x174876e7ff", "0o1351035563777", "0b1011101001000011101101110011111111111"), + nc(100000000000, "100000000000", "0x174876e800", "0o1351035564000", "0b1011101001000011101101110100000000000"), + nc(100000000001, "100000000001", "0x174876e801", "0o1351035564001", "0b1011101001000011101101110100000000001"), + nc(137438953471, "137438953471", "0x1fffffffff", "0o1777777777777", "0b1111111111111111111111111111111111111"), + nc(137438953472, "137438953472", "0x2000000000", "0o2000000000000", "0b10000000000000000000000000000000000000"), + nc(137438953473, "137438953473", "0x2000000001", "0o2000000000001", "0b10000000000000000000000000000000000001"), + nc(184467440736, "184467440736", "0x2af31dc460", "0o2536307342140", "0b10101011110011000111011100010001100000"), + nc(184467440737, "184467440737", "0x2af31dc461", "0o2536307342141", "0b10101011110011000111011100010001100001"), + nc(184467440738, "184467440738", "0x2af31dc462", "0o2536307342142", "0b10101011110011000111011100010001100010"), + nc(274877906943, "274877906943", "0x3fffffffff", "0o3777777777777", "0b11111111111111111111111111111111111111"), + nc(274877906944, "274877906944", "0x4000000000", "0o4000000000000", "0b100000000000000000000000000000000000000"), + nc(274877906945, "274877906945", "0x4000000001", "0o4000000000001", "0b100000000000000000000000000000000000001"), + nc(549755813887, "549755813887", "0x7fffffffff", "0o7777777777777", "0b111111111111111111111111111111111111111"), + nc(549755813888, "549755813888", "0x8000000000", "0o10000000000000", "0b1000000000000000000000000000000000000000"), + nc(549755813889, "549755813889", "0x8000000001", "0o10000000000001", "0b1000000000000000000000000000000000000001"), + nc(999999999999, "999999999999", "0xe8d4a50fff", "0o16432451207777", "0b1110100011010100101001010000111111111111"), + nc(1000000000000, "1000000000000", "0xe8d4a51000", "0o16432451210000", "0b1110100011010100101001010001000000000000"), + nc(1000000000001, "1000000000001", "0xe8d4a51001", "0o16432451210001", "0b1110100011010100101001010001000000000001"), + nc(1099511627775, "1099511627775", "0xffffffffff", "0o17777777777777", "0b1111111111111111111111111111111111111111"), + nc(1099511627776, "1099511627776", "0x10000000000", "0o20000000000000", "0b10000000000000000000000000000000000000000"), + nc(1099511627777, "1099511627777", "0x10000000001", "0o20000000000001", "0b10000000000000000000000000000000000000001"), + nc(1844674407369, "1844674407369", "0x1ad7f29abc9", "0o32657712325711", "0b11010110101111111001010011010101111001001"), + nc(1844674407370, "1844674407370", "0x1ad7f29abca", "0o32657712325712", "0b11010110101111111001010011010101111001010"), + nc(1844674407371, "1844674407371", "0x1ad7f29abcb", "0o32657712325713", "0b11010110101111111001010011010101111001011"), + nc(2199023255551, "2199023255551", "0x1ffffffffff", "0o37777777777777", "0b11111111111111111111111111111111111111111"), + nc(2199023255552, "2199023255552", "0x20000000000", "0o40000000000000", "0b100000000000000000000000000000000000000000"), + nc(2199023255553, "2199023255553", "0x20000000001", "0o40000000000001", "0b100000000000000000000000000000000000000001"), + nc(4398046511103, "4398046511103", "0x3ffffffffff", "0o77777777777777", "0b111111111111111111111111111111111111111111"), + nc(4398046511104, "4398046511104", "0x40000000000", "0o100000000000000", "0b1000000000000000000000000000000000000000000"), + nc(4398046511105, "4398046511105", "0x40000000001", "0o100000000000001", "0b1000000000000000000000000000000000000000001"), + nc(8796093022207, "8796093022207", "0x7ffffffffff", "0o177777777777777", "0b1111111111111111111111111111111111111111111"), + nc(8796093022208, "8796093022208", "0x80000000000", "0o200000000000000", "0b10000000000000000000000000000000000000000000"), + nc(8796093022209, "8796093022209", "0x80000000001", "0o200000000000001", "0b10000000000000000000000000000000000000000001"), + nc(9999999999999, "9999999999999", "0x9184e729fff", "0o221411634517777", "0b10010001100001001110011100101001111111111111"), + nc(10000000000000, "10000000000000", "0x9184e72a000", "0o221411634520000", "0b10010001100001001110011100101010000000000000"), + nc(10000000000001, "10000000000001", "0x9184e72a001", "0o221411634520001", "0b10010001100001001110011100101010000000000001"), + nc(17592186044415, "17592186044415", "0xfffffffffff", "0o377777777777777", "0b11111111111111111111111111111111111111111111"), + nc(17592186044416, "17592186044416", "0x100000000000", "0o400000000000000", "0b100000000000000000000000000000000000000000000"), + nc(17592186044417, "17592186044417", "0x100000000001", "0o400000000000001", "0b100000000000000000000000000000000000000000001"), + nc(18446744073708, "18446744073708", "0x10c6f7a0b5ec", "0o414336750132754", "0b100001100011011110111101000001011010111101100"), + nc(18446744073709, "18446744073709", "0x10c6f7a0b5ed", "0o414336750132755", "0b100001100011011110111101000001011010111101101"), + nc(18446744073710, "18446744073710", "0x10c6f7a0b5ee", "0o414336750132756", "0b100001100011011110111101000001011010111101110"), + nc(35184372088831, "35184372088831", "0x1fffffffffff", "0o777777777777777", "0b111111111111111111111111111111111111111111111"), + nc(35184372088832, "35184372088832", "0x200000000000", "0o1000000000000000", "0b1000000000000000000000000000000000000000000000"), + nc(35184372088833, "35184372088833", "0x200000000001", "0o1000000000000001", "0b1000000000000000000000000000000000000000000001"), + nc(70368744177663, "70368744177663", "0x3fffffffffff", "0o1777777777777777", "0b1111111111111111111111111111111111111111111111"), + nc(70368744177664, "70368744177664", "0x400000000000", "0o2000000000000000", "0b10000000000000000000000000000000000000000000000"), + nc(70368744177665, "70368744177665", "0x400000000001", "0o2000000000000001", "0b10000000000000000000000000000000000000000000001"), + nc(99999999999999, "99999999999999", "0x5af3107a3fff", "0o2657142036437777", "0b10110101111001100010000011110100011111111111111"), + nc(100000000000000, "100000000000000", "0x5af3107a4000", "0o2657142036440000", "0b10110101111001100010000011110100100000000000000"), + nc(100000000000001, "100000000000001", "0x5af3107a4001", "0o2657142036440001", "0b10110101111001100010000011110100100000000000001"), + nc(140737488355327, "140737488355327", "0x7fffffffffff", "0o3777777777777777", "0b11111111111111111111111111111111111111111111111"), + nc(140737488355328, "140737488355328", "0x800000000000", "0o4000000000000000", "0b100000000000000000000000000000000000000000000000"), + nc(140737488355329, "140737488355329", "0x800000000001", "0o4000000000000001", "0b100000000000000000000000000000000000000000000001"), + nc(184467440737094, "184467440737094", "0xa7c5ac471b46", "0o5174265421615506", "0b101001111100010110101100010001110001101101000110"), + nc(184467440737095, "184467440737095", "0xa7c5ac471b47", "0o5174265421615507", "0b101001111100010110101100010001110001101101000111"), + nc(184467440737096, "184467440737096", "0xa7c5ac471b48", "0o5174265421615510", "0b101001111100010110101100010001110001101101001000"), + nc(281474976710655, "281474976710655", "0xffffffffffff", "0o7777777777777777", "0b111111111111111111111111111111111111111111111111"), + nc(281474976710656, "281474976710656", "0x1000000000000", "0o10000000000000000", "0b1000000000000000000000000000000000000000000000000"), + nc(281474976710657, "281474976710657", "0x1000000000001", "0o10000000000000001", "0b1000000000000000000000000000000000000000000000001"), + nc(562949953421311, "562949953421311", "0x1ffffffffffff", "0o17777777777777777", "0b1111111111111111111111111111111111111111111111111"), + nc(562949953421312, "562949953421312", "0x2000000000000", "0o20000000000000000", "0b10000000000000000000000000000000000000000000000000"), + nc(562949953421313, "562949953421313", "0x2000000000001", "0o20000000000000001", "0b10000000000000000000000000000000000000000000000001"), + nc(999999999999999, "999999999999999", "0x38d7ea4c67fff", "0o34327724461477777", "0b11100011010111111010100100110001100111111111111111"), + nc(1000000000000000, "1000000000000000", "0x38d7ea4c68000", "0o34327724461500000", "0b11100011010111111010100100110001101000000000000000"), + nc(1000000000000001, "1000000000000001", "0x38d7ea4c68001", "0o34327724461500001", "0b11100011010111111010100100110001101000000000000001"), + nc(1125899906842623, "1125899906842623", "0x3ffffffffffff", "0o37777777777777777", "0b11111111111111111111111111111111111111111111111111"), + nc(1125899906842624, "1125899906842624", "0x4000000000000", "0o40000000000000000", "0b100000000000000000000000000000000000000000000000000"), + nc(1125899906842625, "1125899906842625", "0x4000000000001", "0o40000000000000001", "0b100000000000000000000000000000000000000000000000001"), + nc(1844674407370954, "1844674407370954", "0x68db8bac710ca", "0o64333427261610312", "0b110100011011011100010111010110001110001000011001010"), + nc(1844674407370955, "1844674407370955", "0x68db8bac710cb", "0o64333427261610313", "0b110100011011011100010111010110001110001000011001011"), + nc(1844674407370956, "1844674407370956", "0x68db8bac710cc", "0o64333427261610314", "0b110100011011011100010111010110001110001000011001100"), + nc(2251799813685247, "2251799813685247", "0x7ffffffffffff", "0o77777777777777777", "0b111111111111111111111111111111111111111111111111111"), + nc(2251799813685248, "2251799813685248", "0x8000000000000", "0o100000000000000000", "0b1000000000000000000000000000000000000000000000000000"), + nc(2251799813685249, "2251799813685249", "0x8000000000001", "0o100000000000000001", "0b1000000000000000000000000000000000000000000000000001"), + nc(4503599627370495, "4503599627370495", "0xfffffffffffff", "0o177777777777777777", "0b1111111111111111111111111111111111111111111111111111"), + nc(4503599627370496, "4503599627370496", "0x10000000000000", "0o200000000000000000", "0b10000000000000000000000000000000000000000000000000000"), + nc(4503599627370497, "4503599627370497", "0x10000000000001", "0o200000000000000001", "0b10000000000000000000000000000000000000000000000000001"), + nc(9007199254740991, "9007199254740991", "0x1fffffffffffff", "0o377777777777777777", "0b11111111111111111111111111111111111111111111111111111"), + nc(9007199254740992, "9007199254740992", "0x20000000000000", "0o400000000000000000", "0b100000000000000000000000000000000000000000000000000000"), + nc(9007199254740993, "9007199254740993", "0x20000000000001", "0o400000000000000001", "0b100000000000000000000000000000000000000000000000000001"), + nc(9999999999999999, "9999999999999999", "0x2386f26fc0ffff", "0o434157115760177777", "0b100011100001101111001001101111110000001111111111111111"), + nc(10000000000000000, "10000000000000000", "0x2386f26fc10000", "0o434157115760200000", "0b100011100001101111001001101111110000010000000000000000"), + nc(10000000000000001, "10000000000000001", "0x2386f26fc10001", "0o434157115760200001", "0b100011100001101111001001101111110000010000000000000001"), + nc(18014398509481983, "18014398509481983", "0x3fffffffffffff", "0o777777777777777777", "0b111111111111111111111111111111111111111111111111111111"), + nc(18014398509481984, "18014398509481984", "0x40000000000000", "0o1000000000000000000", "0b1000000000000000000000000000000000000000000000000000000"), + nc(18014398509481985, "18014398509481985", "0x40000000000001", "0o1000000000000000001", "0b1000000000000000000000000000000000000000000000000000001"), + nc(18446744073709551, "18446744073709551", "0x4189374bc6a7ef", "0o1014223351361523757", "0b1000001100010010011011101001011110001101010011111101111"), + nc(18446744073709552, "18446744073709552", "0x4189374bc6a7f0", "0o1014223351361523760", "0b1000001100010010011011101001011110001101010011111110000"), + nc(18446744073709553, "18446744073709553", "0x4189374bc6a7f1", "0o1014223351361523761", "0b1000001100010010011011101001011110001101010011111110001"), + nc(36028797018963967, "36028797018963967", "0x7fffffffffffff", "0o1777777777777777777", "0b1111111111111111111111111111111111111111111111111111111"), + nc(36028797018963968, "36028797018963968", "0x80000000000000", "0o2000000000000000000", "0b10000000000000000000000000000000000000000000000000000000"), + nc(36028797018963969, "36028797018963969", "0x80000000000001", "0o2000000000000000001", "0b10000000000000000000000000000000000000000000000000000001"), + nc(72057594037927935, "72057594037927935", "0xffffffffffffff", "0o3777777777777777777", "0b11111111111111111111111111111111111111111111111111111111"), + nc(72057594037927936, "72057594037927936", "0x100000000000000", "0o4000000000000000000", "0b100000000000000000000000000000000000000000000000000000000"), + nc(72057594037927937, "72057594037927937", "0x100000000000001", "0o4000000000000000001", "0b100000000000000000000000000000000000000000000000000000001"), + nc(99999999999999999, "99999999999999999", "0x16345785d89ffff", "0o5432127413542377777", "0b101100011010001010111100001011101100010011111111111111111"), + nc(100000000000000000, "100000000000000000", "0x16345785d8a0000", "0o5432127413542400000", "0b101100011010001010111100001011101100010100000000000000000"), + nc(100000000000000001, "100000000000000001", "0x16345785d8a0001", "0o5432127413542400001", "0b101100011010001010111100001011101100010100000000000000001"), + nc(144115188075855871, "144115188075855871", "0x1ffffffffffffff", "0o7777777777777777777", "0b111111111111111111111111111111111111111111111111111111111"), + nc(144115188075855872, "144115188075855872", "0x200000000000000", "0o10000000000000000000", "0b1000000000000000000000000000000000000000000000000000000000"), + nc(144115188075855873, "144115188075855873", "0x200000000000001", "0o10000000000000000001", "0b1000000000000000000000000000000000000000000000000000000001"), + nc(184467440737095519, "184467440737095519", "0x28f5c28f5c28f5f", "0o12172702436560507537", "0b1010001111010111000010100011110101110000101000111101011111"), + nc(184467440737095520, "184467440737095520", "0x28f5c28f5c28f60", "0o12172702436560507540", "0b1010001111010111000010100011110101110000101000111101100000"), + nc(184467440737095521, "184467440737095521", "0x28f5c28f5c28f61", "0o12172702436560507541", "0b1010001111010111000010100011110101110000101000111101100001"), + nc(288230376151711743, "288230376151711743", "0x3ffffffffffffff", "0o17777777777777777777", "0b1111111111111111111111111111111111111111111111111111111111"), + nc(288230376151711744, "288230376151711744", "0x400000000000000", "0o20000000000000000000", "0b10000000000000000000000000000000000000000000000000000000000"), + nc(288230376151711745, "288230376151711745", "0x400000000000001", "0o20000000000000000001", "0b10000000000000000000000000000000000000000000000000000000001"), + nc(576460752303423487, "576460752303423487", "0x7ffffffffffffff", "0o37777777777777777777", "0b11111111111111111111111111111111111111111111111111111111111"), + nc(576460752303423488, "576460752303423488", "0x800000000000000", "0o40000000000000000000", "0b100000000000000000000000000000000000000000000000000000000000"), + nc(576460752303423489, "576460752303423489", "0x800000000000001", "0o40000000000000000001", "0b100000000000000000000000000000000000000000000000000000000001"), + nc(999999999999999999, "999999999999999999", "0xde0b6b3a763ffff", "0o67405553164730777777", "0b110111100000101101101011001110100111011000111111111111111111"), + nc(1000000000000000000, "1000000000000000000", "0xde0b6b3a7640000", "0o67405553164731000000", "0b110111100000101101101011001110100111011001000000000000000000"), + nc(1000000000000000001, "1000000000000000001", "0xde0b6b3a7640001", "0o67405553164731000001", "0b110111100000101101101011001110100111011001000000000000000001"), + nc(1152921504606846975, "1152921504606846975", "0xfffffffffffffff", "0o77777777777777777777", "0b111111111111111111111111111111111111111111111111111111111111"), + nc(1152921504606846976, "1152921504606846976", "0x1000000000000000", "0o100000000000000000000", "0b1000000000000000000000000000000000000000000000000000000000000"), + nc(1152921504606846977, "1152921504606846977", "0x1000000000000001", "0o100000000000000000001", "0b1000000000000000000000000000000000000000000000000000000000001"), + nc(1844674407370955263, "1844674407370955263", "0x19999999999999ff", "0o146314631463146314777", "0b1100110011001100110011001100110011001100110011001100111111111"), + nc(1844674407370955264, "1844674407370955264", "0x1999999999999a00", "0o146314631463146315000", "0b1100110011001100110011001100110011001100110011001101000000000"), + nc(1844674407370955265, "1844674407370955265", "0x1999999999999a01", "0o146314631463146315001", "0b1100110011001100110011001100110011001100110011001101000000001"), + nc(2305843009213693951, "2305843009213693951", "0x1fffffffffffffff", "0o177777777777777777777", "0b1111111111111111111111111111111111111111111111111111111111111"), + nc(2305843009213693952, "2305843009213693952", "0x2000000000000000", "0o200000000000000000000", "0b10000000000000000000000000000000000000000000000000000000000000"), + nc(2305843009213693953, "2305843009213693953", "0x2000000000000001", "0o200000000000000000001", "0b10000000000000000000000000000000000000000000000000000000000001"), + nc(4611686018427387903, "4611686018427387903", "0x3fffffffffffffff", "0o377777777777777777777", "0b11111111111111111111111111111111111111111111111111111111111111"), + nc(4611686018427387904, "4611686018427387904", "0x4000000000000000", "0o400000000000000000000", "0b100000000000000000000000000000000000000000000000000000000000000"), + nc(4611686018427387905, "4611686018427387905", "0x4000000000000001", "0o400000000000000000001", "0b100000000000000000000000000000000000000000000000000000000000001"), + nc(9223372036854775807, "9223372036854775807", "0x7fffffffffffffff", "0o777777777777777777777", "0b111111111111111111111111111111111111111111111111111111111111111"), + nc(9223372036854775808, "9223372036854775808", "0x8000000000000000", "0o1000000000000000000000", "0b1000000000000000000000000000000000000000000000000000000000000000"), + nc(9223372036854775809, "9223372036854775809", "0x8000000000000001", "0o1000000000000000000001", "0b1000000000000000000000000000000000000000000000000000000000000001"), + nc(9999999999999999999, "9999999999999999999", "0x8ac7230489e7ffff", "0o1053071060221171777777", "0b1000101011000111001000110000010010001001111001111111111111111111"), + nc(10000000000000000000, "10000000000000000000", "0x8ac7230489e80000", "0o1053071060221172000000", "0b1000101011000111001000110000010010001001111010000000000000000000"), + nc(10000000000000000001, "10000000000000000001", "0x8ac7230489e80001", "0o1053071060221172000001", "0b1000101011000111001000110000010010001001111010000000000000000001"), + nc(18446744073709551611, "18446744073709551611", "0xfffffffffffffffb", "0o1777777777777777777773", "0b1111111111111111111111111111111111111111111111111111111111111011"), + nc(18446744073709551612, "18446744073709551612", "0xfffffffffffffffc", "0o1777777777777777777774", "0b1111111111111111111111111111111111111111111111111111111111111100"), + nc(18446744073709551613, "18446744073709551613", "0xfffffffffffffffd", "0o1777777777777777777775", "0b1111111111111111111111111111111111111111111111111111111111111101"), + nc(18446744073709551614, "18446744073709551614", "0xfffffffffffffffe", "0o1777777777777777777776", "0b1111111111111111111111111111111111111111111111111111111111111110"), + nc(18446744073709551615, "18446744073709551615", "0xffffffffffffffff", "0o1777777777777777777777", "0b1111111111111111111111111111111111111111111111111111111111111111"), +#undef nc + }; +}; + +C4_INLINE_CONSTEXPR const number_case<int8_t> numbers<int8_t>::vals[]; +C4_INLINE_CONSTEXPR const number_case<uint8_t> numbers<uint8_t>::vals[]; + +C4_INLINE_CONSTEXPR const number_case<int16_t> numbers<int16_t>::vals[]; +C4_INLINE_CONSTEXPR const number_case<uint16_t> numbers<uint16_t>::vals[]; + +C4_INLINE_CONSTEXPR const number_case<int32_t> numbers<int32_t>::vals[]; +C4_INLINE_CONSTEXPR const number_case<uint32_t> numbers<uint32_t>::vals[]; + +C4_INLINE_CONSTEXPR const number_case<int64_t> numbers<int64_t>::vals[]; +C4_INLINE_CONSTEXPR const number_case<uint64_t> numbers<uint64_t>::vals[]; + +C4_SUPPRESS_WARNING_MSVC_POP + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/test/test_preprocessor.cpp b/thirdparty/ryml/ext/c4core/test/test_preprocessor.cpp new file mode 100644 index 000000000..bd2619ae4 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_preprocessor.cpp @@ -0,0 +1,55 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/preprocessor.hpp" +#include "c4/language.hpp" +#endif + +#ifdef WE_LL_GET_THERE___MSVC_CANT_HANDLE_THE_FOREACH_MACRO___NEEDS_TO_BE_FIXED +#include <string> +#include <map> + +struct SomeStruct +{ + int32_t a; + int32_t b; + int32_t c; + int32_t d; +}; + +TEST(TestForEach, print_offsets) +{ +#define M_OFFS_(structure, field) m[#field] = offsetof(structure, field) +#define M_OFFS(field) M_OFFS_(SomeStruct, field) + + std::map< std::string, size_t > m; + + C4_FOR_EACH(M_OFFS, a, b, c); + C4_FOR_EACH(M_OFFS, d); + + EXPECT_EQ(m["a"], 0); + EXPECT_EQ(m["b"], 4); + EXPECT_EQ(m["c"], 8); + EXPECT_EQ(m["d"], 12); +} + +//----------------------------------------------------------------------------- +// C4_BEGIN_NAMESPACE()/C4_END_NAMESPACE() are implemented with C4_FOR_EACH(). +// Test these here too. + +namespace a, b, c { +int a_var = 0; +} // namespace c, b +int var = 1; // a::var +namespace b { +int var = 2; // a::b::var +namespace c { +int var = 3; // a::b::c::var +} // namespace c, b, a + +TEST(TestForEach, begin_end_namespace) +{ + EXPECT_EQ(a::b::c::a_var, 0); + EXPECT_EQ(a::var, 1); + EXPECT_EQ(a::b::var, 2); + EXPECT_EQ(a::b::c::var, 3); +} +#endif diff --git a/thirdparty/ryml/ext/c4core/test/test_singleheader/libc4core_singleheader.cpp b/thirdparty/ryml/ext/c4core/test/test_singleheader/libc4core_singleheader.cpp new file mode 100644 index 000000000..078c77d1e --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_singleheader/libc4core_singleheader.cpp @@ -0,0 +1,2 @@ +#define C4CORE_SINGLE_HDR_DEFINE_NOW +#include <c4/c4core_all.hpp> diff --git a/thirdparty/ryml/ext/c4core/test/test_span.cpp b/thirdparty/ryml/ext/c4core/test/test_span.cpp new file mode 100644 index 000000000..c492013d7 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_span.cpp @@ -0,0 +1,944 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/span.hpp" +#endif + +#include "c4/libtest/supprwarn_push.hpp" + +#include <c4/test.hpp> + +namespace c4 { + +//----------------------------------------------------------------------------- +TEST_CASE_TEMPLATE("span.default_init", SpanClass, span<int>, spanrs<int>, spanrsl<int>) +{ + SpanClass s; + CHECK_EQ(s.size(), 0); + CHECK_EQ(s.capacity(), 0); + CHECK_EQ(s.data(), nullptr); +} + + +template<template<class, class> class Span, class T, class I> +Span<const T, I> cvt_to_const(Span<T, I> const& s) +{ + Span<const T, I> ret = s; + return ret; +} + +TEST_CASE_TEMPLATE("span.convert_to_const", SpanClass, span<int>, spanrs<int>, spanrsl<int>) +{ + SpanClass s; + auto cs = cvt_to_const(s); + CHECK_EQ(s.size(), cs.size()); + CHECK_EQ(s.data(), cs.data()); + CHECK_EQ(s.end(), cs.end()); +} + + +//----------------------------------------------------------------------------- +TEST_CASE("span.empty_init") +{ + int arr[10]; + span<int> s(arr, 0); + CHECK_EQ(s.size(), 0); + CHECK_EQ(s.capacity(), 0); + CHECK_NE(s.data(), nullptr); +} + +TEST_CASE("spanrs.empty_init") +{ + int arr[10]; + + { + spanrs<int> s(arr, 0); + CHECK_EQ(s.size(), 0); + CHECK_EQ(s.capacity(), 0); + CHECK_EQ(s.data(), arr); + } + + { + spanrs<int> s(arr, 0, C4_COUNTOF(arr)); + CHECK_EQ(s.size(), 0); + CHECK_EQ(s.capacity(), 10); + CHECK_EQ(s.data(), arr); + } +} + +TEST_CASE("spanrsl.empty_init") +{ + int arr[10]; + + { + spanrsl<int> s(arr, 0); + CHECK_EQ(s.size(), 0); + CHECK_EQ(s.capacity(), 0); + CHECK_EQ(s.data(), arr); + CHECK_EQ(s.offset(), 0); + } + + { + spanrsl<int> s(arr, 0, C4_COUNTOF(arr)); + CHECK_EQ(s.size(), 0); + CHECK_EQ(s.capacity(), 10); + CHECK_EQ(s.data(), arr); + CHECK_EQ(s.offset(), 0); + } +} + +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("span.fromArray", SpanClass, + span<char>, span<int>, span<uint32_t>, + spanrs<char>, spanrs<int>, spanrs<uint32_t>, + spanrsl<char>, spanrsl<int>, spanrsl<uint32_t> + ) +{ + using ConstSpanClass = typename SpanClass::const_type; + using T = typename SpanClass::value_type; + T arr1[10]; + T arr2[20]; + + T a = 0; + for(auto &v : arr1) { v = a; ++a; } + for(auto &v : arr2) { v = a; ++a; } + + { + SpanClass s(arr1); + CHECK_EQ(s.size(), C4_COUNTOF(arr1)); + CHECK_EQ(s.capacity(), C4_COUNTOF(arr1)); + CHECK_EQ(s.data(), arr1); + } + + { + ConstSpanClass s(arr1); + CHECK_EQ(s.size(), C4_COUNTOF(arr1)); + CHECK_EQ(s.capacity(), C4_COUNTOF(arr1)); + CHECK_EQ(s.data(), arr1); + } + + { + SpanClass s = arr1; + CHECK_EQ(s.size(), C4_COUNTOF(arr1)); + CHECK_EQ(s.capacity(), C4_COUNTOF(arr1)); + CHECK_EQ(s.data(), arr1); + } + + { + ConstSpanClass s = arr1; + CHECK_EQ(s.size(), C4_COUNTOF(arr1)); + CHECK_EQ(s.capacity(), C4_COUNTOF(arr1)); + CHECK_EQ(s.data(), arr1); + } + + { + SpanClass s = arr1; + CHECK_EQ(s.size(), C4_COUNTOF(arr1)); + CHECK_EQ(s.capacity(), C4_COUNTOF(arr1)); + CHECK_EQ(s.data(), arr1); + s = arr2; + CHECK_EQ(s.size(), C4_COUNTOF(arr2)); + CHECK_EQ(s.capacity(), C4_COUNTOF(arr2)); + CHECK_EQ(s.data(), arr2); + } + + { + ConstSpanClass s = arr1; + CHECK_EQ(s.size(), C4_COUNTOF(arr1)); + CHECK_EQ(s.capacity(), C4_COUNTOF(arr1)); + CHECK_EQ(s.data(), arr1); + s = arr2; + CHECK_EQ(s.size(), C4_COUNTOF(arr2)); + CHECK_EQ(s.capacity(), C4_COUNTOF(arr2)); + CHECK_EQ(s.data(), arr2); + } +} + + +//----------------------------------------------------------------------------- +TEST_CASE("span.subspan") +{ + int arr[10]; + span<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.subspan(0)), decltype(s)>::value)); + + { + auto ss = s.subspan(0, 5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), arr); + + ss = s.subspan(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), &arr[5]); + + // fine to obtain an empty span at the end + ss = s.subspan(10); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), std::end(arr)); + // fine to obtain an empty span at the end + ss = s.subspan(10, 0); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), std::end(arr)); + } + { + int buf10[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + int buf_5[] = {-1, 0, 1, 2, 3, 4}; + int *buf5 = buf_5 + 1; // to make sure that one does not immediately follow the other in memory + + span<int> n(buf10); + span<int> m(buf5, 5); + + auto ss = n.subspan(0); + CHECK_EQ(ss.data(), buf10); + CHECK_EQ(ss.size(), 10); + ss = m.subspan(0); + CHECK_EQ(ss.data(), buf5); + CHECK_EQ(ss.size(), 5); + ss = n.subspan(0, 0); + CHECK_NE(ss.data(), nullptr); + CHECK_EQ(ss.data(), &buf10[0]); + CHECK_EQ(ss.size(), 0); + ss = m.subspan(0, 0); + CHECK_NE(ss.data(), nullptr); + CHECK_EQ(ss.data(), &buf5[0]); + CHECK_EQ(ss.size(), 0); + } +} +TEST_CASE("spanrs.subspan") +{ + int arr[10]; + spanrs<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.subspan(0)), decltype(s)>::value)); + + auto ss = s.subspan(0, 5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = s.subspan(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), &arr[5]); + + // fine to obtain an empty span at the end + ss = s.subspan(10); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), std::end(arr)); + // fine to obtain an empty span at the end + ss = s.subspan(10, 0); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), std::end(arr)); +} +TEST_CASE("spanrsl.subspan") +{ + int arr[10]; + spanrsl<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.subspan(0)), decltype(s)>::value)); + + auto ss = s.subspan(0, 5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + CHECK_EQ(ss.offset(), 0); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + CHECK_EQ(ss.offset(), 0); + + ss = s.subspan(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), &arr[5]); + CHECK_EQ(ss.offset(), 5); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + CHECK_EQ(ss.offset(), 0); + + // fine to obtain an empty span at the end + ss = s.subspan(10); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), std::end(arr)); + // fine to obtain an empty span at the end + ss = s.subspan(10, 0); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), std::end(arr)); +} + +//----------------------------------------------------------------------------- +TEST_CASE("span.range") +{ + int arr[10]; + span<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.range(0)), decltype(s)>::value)); + + auto ss = s.range(0, 5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), arr); + + ss = s.range(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), &arr[5]); + + ss = s.range(5, 10); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), &arr[5]); + + ss = s.range(10, 10); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), std::end(arr)); + + SUBCASE("empty_span") + { + s = {}; + ss = s.range(0, 0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), nullptr); + s = arr; + ss = s.range(0, 0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr); + ss = s.range(10, 10); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr + 10); + ss = s.range(10); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr + 10); + } +} +TEST_CASE("spanrs.range") +{ + int arr[10]; + spanrs<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.range(0)), decltype(s)>::value)); + + auto ss = s.range(0, 5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = s.range(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), &arr[5]); + + ss = s.range(5, 10); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), &arr[5]); + + ss = s.range(10, 10); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), std::end(arr)); + + SUBCASE("empty_span") + { + s = {}; + ss = s.range(0, 0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), nullptr); + s = arr; + ss = s.range(0, 0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr); + ss = s.range(10, 10); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr + 10); + ss = s.range(10); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr + 10); + } +} +TEST_CASE("spanrsl.range") +{ + int arr[10]; + spanrsl<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.range(0)), decltype(s)>::value)); + + auto ss = s.range(0, 5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = s.range(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), &arr[5]); + + ss = s.range(5, 10); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), &arr[5]); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = s.range(10, 10); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), std::end(arr)); + + SUBCASE("empty_span") + { + s = {}; + ss = s.range(0, 0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), nullptr); + s = arr; + ss = s.range(0, 0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr); + ss = s.range(10, 10); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr + 10); + ss = s.range(10); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr + 10); + } +} + +//----------------------------------------------------------------------------- +TEST_CASE("span.first") +{ + int arr[10]; + span<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.first(1)), decltype(s)>::value)); + + auto ss = s.first(0); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), arr); + + ss = s.first(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), arr); + + SUBCASE("empty") + { + s = {}; + ss = s.first(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), nullptr); + s = arr; + ss = s.first(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr); + } +} +TEST_CASE("spanrs.first") +{ + int arr[10]; + spanrs<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.first(1)), decltype(s)>::value)); + + auto ss = s.first(0); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = s.first(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + SUBCASE("empty") + { + s = {}; + ss = s.first(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), nullptr); + s = arr; + ss = s.first(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr); + } +} +TEST_CASE("spanrsl.first") +{ + int arr[10]; + spanrsl<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.first(1)), decltype(s)>::value)); + + auto ss = s.first(0); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = s.first(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + SUBCASE("empty") + { + s = {}; + ss = s.first(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), nullptr); + s = arr; + ss = s.first(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr); + } +} + +//----------------------------------------------------------------------------- +TEST_CASE("span.last") +{ + int arr[10]; + span<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.last(1)), decltype(s)>::value)); + + auto ss = s.last(0); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), arr + s.size()); + + ss = s.last(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), arr + 5); + + SUBCASE("empty") + { + s = {}; + ss = s.last(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), nullptr); + s = arr; + ss = s.last(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr + 10); + } +} +TEST_CASE("spanrs.last") +{ + int arr[10]; + spanrs<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.last(1)), decltype(s)>::value)); + + auto ss = s.last(0); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), arr + s.size()); + + ss = s.last(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), arr + 5); + + SUBCASE("empty") + { + s = {}; + ss = s.last(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), nullptr); + s = arr; + ss = s.last(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr + 10); + } +} +TEST_CASE("spanrsl.last") +{ + int arr[10]; + spanrsl<int> s(arr); + C4_STATIC_ASSERT((std::is_same<decltype(s.last(1)), decltype(s)>::value)); + + auto ss = s.last(0); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.capacity(), 0); + CHECK_EQ(ss.data(), arr + s.size()); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss = s.last(5); + CHECK_EQ(ss.size(), 5); + CHECK_EQ(ss.capacity(), 5); + CHECK_EQ(ss.data(), arr + 5); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + SUBCASE("empty") + { + s = {}; + ss = s.last(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), nullptr); + s = arr; + ss = s.last(0); + CHECK(ss.empty()); + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.data(), arr + 10); + } +} + + +//----------------------------------------------------------------------------- +TEST_CASE_TEMPLATE("span.is_subspan", SpanClass, span<int>, spanrs<int>, spanrsl<int>) +{ + int buf10[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + int buf_5[] = {-1, 0, 1, 2, 3, 4}; + int *buf5 = buf_5 + 1; // to make sure that one does not immediately follow the other in memory + + SpanClass n(buf10); + SpanClass m(buf5, 5); + + CHECK_EQ(n.data(), buf10); + CHECK_EQ(m.data(), buf5); + + CHECK_UNARY(n.is_subspan(n.subspan(0 ))); + CHECK_UNARY(n.is_subspan(n.subspan(0, 3))); + CHECK_UNARY(n.is_subspan(n.subspan(0, 0))); + + CHECK_FALSE(n.is_subspan(m.subspan(0 ))); + CHECK_FALSE(n.is_subspan(m.subspan(0, 3))); + CHECK_FALSE(n.is_subspan(m.subspan(0, 0))); +} + +//----------------------------------------------------------------------------- +TEST_CASE_TEMPLATE("span.compll", SpanClass, span<int>, spanrs<int>, spanrsl<int>) +{ + int buf10[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + SpanClass n(buf10); + + CHECK_EQ(n.compll(n.subspan(0)), n.subspan(0, 0)); + CHECK_EQ(n.is_subspan(n.compll(n.subspan(0))), true); + CHECK_EQ(n.compll(n.subspan(0, 0)), n.subspan(0, 0)); + CHECK_EQ(n.is_subspan(n.compll(n.subspan(0, 0))), true); + + CHECK_EQ(n.compll(n.subspan(0, 1)), n.subspan(0, 0)); + CHECK_EQ(n.compll(n.subspan(0, 3)), n.subspan(0, 0)); + + CHECK_EQ(n.compll(n.range(5, 10)), n.subspan(0, 5)); + CHECK_EQ(n.compll(n.range(5, 5)), n.subspan(0, 5)); + + CHECK_EQ(n.compll(n.subspan(n.size(), 0)), n); + CHECK_EQ(n.compll(n.range(n.size(), n.size())), n); +} + + +//----------------------------------------------------------------------------- + +TEST_CASE_TEMPLATE("span.complr", SpanClass, span<int>, spanrs<int>, spanrsl<int>) +{ + int buf10[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + SpanClass n(buf10); + + CHECK_EQ(n.complr(n.subspan(0)), n.subspan(0, 0)); + CHECK_EQ(n.is_subspan(n.complr(n.subspan(0))), true); + CHECK_EQ(n.complr(n.subspan(0, 0)), n.subspan(0)); + CHECK_EQ(n.is_subspan(n.complr(n.subspan(0, 0))), true); + + CHECK_EQ(n.complr(n.subspan(0, 1)), n.subspan(1)); + CHECK_EQ(n.complr(n.subspan(0, 3)), n.subspan(3)); + + CHECK_EQ(n.complr(n.subspan(5)), n.subspan(0, 0)); + CHECK_EQ(n.complr(n.range(5, 10)), n.subspan(0, 0)); + + CHECK_EQ(n.complr(n.subspan(5, 0)), n.subspan(5)); + CHECK_EQ(n.complr(n.range(5, 5)), n.subspan(5)); + + CHECK_EQ(n.complr(n.subspan(0, 0)), n); + CHECK_EQ(n.complr(n.range(0, 0)), n); +} + + +//----------------------------------------------------------------------------- +TEST_CASE("span.rtrim") +{ + int arr[10]; + span<int> s(arr); + auto ss = s; + + ss.rtrim(0); + CHECK_EQ(ss.size(), s.size()); + CHECK_EQ(ss.capacity(), s.capacity()); + CHECK_EQ(ss.data(), arr); + + ss.rtrim(5); + CHECK_EQ(ss.size(), s.size() - 5); + CHECK_EQ(ss.capacity(), s.capacity() - 5); + CHECK_EQ(ss.data(), arr); +} +TEST_CASE("spanrs.rtrim") +{ + int arr[10]; + spanrs<int> s(arr); + auto ss = s; + + ss.rtrim(0); + CHECK_EQ(ss.size(), s.size()); + CHECK_EQ(ss.capacity(), s.capacity()); + CHECK_EQ(ss.data(), arr); + + ss.rtrim(5); + CHECK_EQ(ss.size(), s.size() - 5); + CHECK_EQ(ss.capacity(), s.capacity()); + CHECK_EQ(ss.data(), arr); +} +TEST_CASE("spanrsl.rtrim") +{ + int arr[10]; + spanrsl<int> s(arr); + auto ss = s; + + ss.rtrim(0); + CHECK_EQ(ss.size(), s.size()); + CHECK_EQ(ss.capacity(), s.capacity()); + CHECK_EQ(ss.data(), arr); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss.rtrim(5); + CHECK_EQ(ss.size(), s.size() - 5); + CHECK_EQ(ss.capacity(), s.capacity()); + CHECK_EQ(ss.data(), arr); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); +} + +//----------------------------------------------------------------------------- +TEST_CASE("span.ltrim") +{ + int arr[10]; + span<int> s(arr); + auto ss = s; + + ss.ltrim(0); + CHECK_EQ(ss.size(), s.size()); + CHECK_EQ(ss.capacity(), s.capacity()); + CHECK_EQ(ss.data(), arr); + + ss.ltrim(5); + CHECK_EQ(ss.size(), s.size() - 5); + CHECK_EQ(ss.capacity(), s.capacity() - 5); + CHECK_EQ(ss.data(), arr + 5); +} +TEST_CASE("spanrs.ltrim") +{ + int arr[10]; + spanrs<int> s(arr); + auto ss = s; + + ss.ltrim(0); + CHECK_EQ(ss.size(), s.size()); + CHECK_EQ(ss.capacity(), ss.capacity()); + CHECK_EQ(ss.data(), arr); + + ss.ltrim(5); + CHECK_EQ(ss.size(), s.size() - 5); + CHECK_EQ(ss.capacity(), s.size() - 5); + CHECK_EQ(ss.data(), arr + 5); +} +TEST_CASE("spanrsl.ltrim") +{ + int arr[10]; + spanrsl<int> s(arr); + auto ss = s; + + ss.ltrim(0); + CHECK_EQ(ss.size(), s.size()); + CHECK_EQ(ss.capacity(), ss.capacity()); + CHECK_EQ(ss.data(), arr); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); + + ss.ltrim(5); + CHECK_EQ(ss.size(), s.size() - 5); + CHECK_EQ(ss.capacity(), s.size() - 5); + CHECK_EQ(ss.data(), arr + 5); + + ss = ss.original(); + CHECK_EQ(ss.size(), 10); + CHECK_EQ(ss.capacity(), 10); + CHECK_EQ(ss.data(), arr); +} + +//----------------------------------------------------------------------------- +const char larrc[11] = "0123456789"; +const char rarrc[11] = "1234567890"; +const int larri[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +const int rarri[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; +TEST_CASE("span.reverse_iter") +{ + cspan<int> s(larri); + REQUIRE_EQ(s.data(), larri); + REQUIRE_EQ(s.begin(), std::begin(larri)); + REQUIRE_EQ(s.end(), std::end(larri)); + REQUIRE_EQ(&*s.rbegin(), s.end()-1); + using rit = cspan<int>::const_reverse_iterator; + int pos = szconv<int>(s.size()) - 1; + size_t count = 0; + //for(rit b = s.rbegin(), e = s.rend(); b != e; ++b) // BUG: b != e is never true on arm-eabi-g++-7 + for(rit b = s.rbegin(), e = s.rend(); b < e; ++b) + { + CHECK_EQ(&(*b), s.data() + pos); + auto spos = szconv<size_t>(pos); + REQUIRE_EQ(&*b, &s[spos]); + REQUIRE_GE(pos, 0); + REQUIRE_LT(pos, szconv<int>(s.size())); + REQUIRE_LT(count, s.size()); + CHECK_EQ(*b, s[spos]); + --pos; + ++count; + } + CHECK_EQ(pos, -1); + CHECK_EQ(count, s.size()); +} + +//----------------------------------------------------------------------------- +TEST_CASE("span_impl.eq") +{ + CHECK_EQ(cspan <char>(larrc), cspan <char>(larrc)); + CHECK_EQ(cspanrs<char>(larrc), cspan <char>(larrc)); + CHECK_EQ(cspan <char>(larrc), cspanrs<char>(larrc)); + CHECK_EQ(cspanrs<char>(larrc), cspanrs<char>(larrc)); + + CHECK_EQ(cspan <int>(larri) , cspan <int>(larri)); + CHECK_EQ(cspanrs<int>(larri) , cspan <int>(larri)); + CHECK_EQ(cspan <int>(larri) , cspanrs<int>(larri)); + CHECK_EQ(cspanrs<int>(larri) , cspanrs<int>(larri)); +} + +TEST_CASE("span_impl.lt") +{ + CHECK_LT(cspan <char>(larrc), cspan <char>(rarrc)); + CHECK_LT(cspanrs<char>(larrc), cspan <char>(rarrc)); + CHECK_LT(cspan <char>(larrc), cspanrs<char>(rarrc)); + CHECK_LT(cspanrs<char>(larrc), cspanrs<char>(rarrc)); + + CHECK_LT(cspan <int>(larri) , cspan <int>(rarri)); + CHECK_LT(cspanrs<int>(larri) , cspan <int>(rarri)); + CHECK_LT(cspan <int>(larri) , cspanrs<int>(rarri)); + CHECK_LT(cspanrs<int>(larri) , cspanrs<int>(rarri)); +} +TEST_CASE("span_impl.gt") +{ + CHECK_GT(cspan <char>(rarrc), cspan <char>(larrc)); + CHECK_GT(cspan <char>(rarrc), cspanrs<char>(larrc)); + CHECK_GT(cspanrs<char>(rarrc), cspan <char>(larrc)); + CHECK_GT(cspanrs<char>(rarrc), cspanrs<char>(larrc)); + + CHECK_GT(cspan <int>(rarri) , cspan <int>(larri)); + CHECK_GT(cspan <int>(rarri) , cspanrs<int>(larri)); + CHECK_GT(cspanrs<int>(rarri) , cspan <int>(larri)); + CHECK_GT(cspanrs<int>(rarri) , cspanrs<int>(larri)); +} + +TEST_CASE("span_impl.ge") +{ + CHECK_GE(cspan <char>(rarrc), cspan <char>(larrc)); + CHECK_GE(cspan <char>(rarrc), cspanrs<char>(larrc)); + CHECK_GE(cspanrs<char>(rarrc), cspan <char>(larrc)); + CHECK_GE(cspanrs<char>(rarrc), cspanrs<char>(larrc)); + CHECK_GE(cspan <char>(larrc), cspan <char>(larrc)); + CHECK_GE(cspan <char>(larrc), cspanrs<char>(larrc)); + CHECK_GE(cspanrs<char>(larrc), cspan <char>(larrc)); + CHECK_GE(cspanrs<char>(larrc), cspanrs<char>(larrc)); + CHECK_GE(cspan <int>(rarri) , cspan <int>(larri)); + CHECK_GE(cspan <int>(rarri) , cspanrs<int>(larri)); + CHECK_GE(cspanrs<int>(rarri) , cspan <int>(larri)); + CHECK_GE(cspanrs<int>(rarri) , cspanrs<int>(larri)); + CHECK_GE(cspan <int>(larri) , cspan <int>(larri)); + CHECK_GE(cspan <int>(larri) , cspanrs<int>(larri)); + CHECK_GE(cspanrs<int>(larri) , cspan <int>(larri)); + CHECK_GE(cspanrs<int>(larri) , cspanrs<int>(larri)); +} +TEST_CASE("span_impl.le") +{ + CHECK_LE(cspan <char>(larrc), cspan <char>(rarrc)); + CHECK_LE(cspanrs<char>(larrc), cspan <char>(rarrc)); + CHECK_LE(cspan <char>(larrc), cspanrs<char>(rarrc)); + CHECK_LE(cspanrs<char>(larrc), cspanrs<char>(rarrc)); + CHECK_LE(cspan <char>(larrc), cspan <char>(larrc)); + CHECK_LE(cspanrs<char>(larrc), cspan <char>(larrc)); + CHECK_LE(cspan <char>(larrc), cspanrs<char>(larrc)); + CHECK_LE(cspanrs<char>(larrc), cspanrs<char>(larrc)); + CHECK_LE(cspan <int>(larri) , cspan <int>(rarri)); + CHECK_LE(cspanrs<int>(larri) , cspan <int>(rarri)); + CHECK_LE(cspan <int>(larri) , cspanrs<int>(rarri)); + CHECK_LE(cspanrs<int>(larri) , cspanrs<int>(rarri)); + CHECK_LE(cspan <int>(larri) , cspan <int>(larri)); + CHECK_LE(cspanrs<int>(larri) , cspan <int>(larri)); + CHECK_LE(cspan <int>(larri) , cspanrs<int>(larri)); + CHECK_LE(cspanrs<int>(larri) , cspanrs<int>(larri)); +} + +} // namespace c4 + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_std_string.cpp b/thirdparty/ryml/ext/c4core/test/test_std_string.cpp new file mode 100644 index 000000000..de5b3739b --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_std_string.cpp @@ -0,0 +1,125 @@ +#include "c4/test.hpp" +#ifndef C4CORE_SINGLE_HEADER +#include "c4/std/string_fwd.hpp" +#include "c4/std/string.hpp" +#endif + +namespace c4 { + +TEST_CASE("std_string.to_substr") +{ + std::string s("barnabe"); + substr ss = to_substr(s); + CHECK_EQ(ss.str, s.data()); + CHECK_EQ(ss.len, s.size()); + s[0] = 'B'; + CHECK_EQ(ss[0], 'B'); + ss[0] = 'b'; + CHECK_EQ(s[0], 'b'); +} + +TEST_CASE("std_string.to_csubstr") +{ + std::string s("barnabe"); + csubstr ss = to_csubstr(s); + CHECK_EQ(ss.str, s.data()); + CHECK_EQ(ss.len, s.size()); + s[0] = 'B'; + CHECK_EQ(ss[0], 'B'); +} + +TEST_CASE("std_string.compare_csubstr") +{ + std::string s0 = "000"; + std::string s1 = "111"; + csubstr ss0 = "000"; + csubstr ss1 = "111"; + CHECK_NE(s0.data(), ss0.data()); + CHECK_NE(s1.data(), ss1.data()); + // + CHECK_EQ(s0, ss0); + CHECK_EQ(s1, ss1); + CHECK_EQ(ss0, s0); + CHECK_EQ(ss1, s1); + // + CHECK_NE(s1, ss0); + CHECK_NE(s0, ss1); + CHECK_NE(ss1, s0); + CHECK_NE(ss0, s1); + // + CHECK_GE(s0, ss0); + CHECK_LE(s1, ss1); + CHECK_GE(ss0, s0); + CHECK_LE(ss1, s1); + CHECK_GE(s1, ss0); + CHECK_LE(s0, ss1); + CHECK_GE(ss1, s0); + CHECK_LE(ss0, s1); + // + CHECK_GT(s1, ss0); + CHECK_LT(s0, ss1); + CHECK_GT(ss1, s0); + CHECK_LT(ss0, s1); +} + +TEST_CASE("std_string.compare_substr") +{ + std::string s0 = "000"; + std::string s1 = "111"; + char buf0[] = "000"; + char buf1[] = "111"; + substr ss0 = buf0; + substr ss1 = buf1; + CHECK_NE(s0.data(), ss0.data()); + CHECK_NE(s1.data(), ss1.data()); + // + CHECK_EQ(s0, ss0); + CHECK_EQ(s1, ss1); + CHECK_EQ(ss0, s0); + CHECK_EQ(ss1, s1); + // + CHECK_NE(s1, ss0); + CHECK_NE(s0, ss1); + CHECK_NE(ss1, s0); + CHECK_NE(ss0, s1); + // + CHECK_GE(s0, ss0); + CHECK_LE(s1, ss1); + CHECK_GE(ss0, s0); + CHECK_LE(ss1, s1); + CHECK_GE(s1, ss0); + CHECK_LE(s0, ss1); + CHECK_GE(ss1, s0); + CHECK_LE(ss0, s1); + // + CHECK_GT(s1, ss0); + CHECK_LT(s0, ss1); + CHECK_GT(ss1, s0); + CHECK_LT(ss0, s1); +} + +TEST_CASE("std_string.to_chars") +{ + const std::string s0 = "000"; + char buf_[100] = {}; + substr buf = buf_; + CHECK_NE(buf.data(), s0.data()); + size_t ret = to_chars({}, s0); + CHECK_EQ(ret, s0.size()); + CHECK_NE(buf.first(ret), s0); + ret = to_chars(buf, s0); + CHECK_EQ(ret, s0.size()); + CHECK_EQ(buf.first(ret), s0); +} + +TEST_CASE("std_string.from_chars") +{ + std::string s0; + csubstr buf = "0123456798"; + CHECK_NE(buf.data(), s0.data()); + bool ok = from_chars(buf, &s0); + CHECK(ok); + CHECK_EQ(buf, s0); +} + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/test/test_std_vector.cpp b/thirdparty/ryml/ext/c4core/test/test_std_vector.cpp new file mode 100644 index 000000000..1cfe54f94 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_std_vector.cpp @@ -0,0 +1,133 @@ +#include "c4/test.hpp" +#ifndef C4CORE_SINGLE_HEADER +#include "c4/std/vector_fwd.hpp" +#include "c4/std/vector.hpp" +#endif + +namespace c4 { + +template<size_t N> +std::vector<char> ctor(const char (&s)[N]) +{ + return std::vector<char>(s, s+N-1); +} + +TEST_CASE("std_vector.to_csubstr") +{ + std::vector<char> s = ctor("barnabe"); + csubstr ss = to_csubstr(s); + CHECK_EQ(ss, csubstr("barnabe")); + CHECK_EQ(ss.str, s.data()); + CHECK_EQ(ss.len, s.size()); +} + +TEST_CASE("std_vector.to_substr") +{ + std::vector<char> s = ctor("barnabe"); + substr ss = to_substr(s); + CHECK_EQ(ss, csubstr("barnabe")); + CHECK_EQ(ss.str, s.data()); + CHECK_EQ(ss.len, s.size()); + // + CHECK_EQ(s[0], 'b'); + ss[0] = 'B'; + CHECK_EQ(s[0], 'B'); + ss[0] = 'A'; + CHECK_EQ(s[0], 'A'); +} + +TEST_CASE("std_vector.compare_csubstr") +{ + std::vector<char> s0 = ctor("000"); + std::vector<char> s1 = ctor("111"); + csubstr ss0 = "000"; + csubstr ss1 = "111"; + CHECK_NE(s0.data(), ss0.data()); + CHECK_NE(s1.data(), ss1.data()); + // + CHECK_EQ(s0, ss0); + CHECK_EQ(s1, ss1); + CHECK_EQ(ss0, s0); + CHECK_EQ(ss1, s1); + // + CHECK_NE(s1, ss0); + CHECK_NE(s0, ss1); + CHECK_NE(ss1, s0); + CHECK_NE(ss0, s1); + // + CHECK_GE(s0, ss0); + CHECK_LE(s1, ss1); + CHECK_GE(ss0, s0); + CHECK_LE(ss1, s1); + CHECK_GE(s1, ss0); + CHECK_LE(s0, ss1); + CHECK_GE(ss1, s0); + CHECK_LE(ss0, s1); + // + CHECK_GT(s1, ss0); + CHECK_LT(s0, ss1); + CHECK_GT(ss1, s0); + CHECK_LT(ss0, s1); +} + +TEST_CASE("std_vector.compare_substr") +{ + std::vector<char> s0 = ctor("000"); + std::vector<char> s1 = ctor("111"); + char buf0[] = "000"; + char buf1[] = "111"; + substr ss0 = buf0; + substr ss1 = buf1; + CHECK_NE(s0.data(), ss0.data()); + CHECK_NE(s1.data(), ss1.data()); + // + CHECK_EQ(s0, ss0); + CHECK_EQ(s1, ss1); + CHECK_EQ(ss0, s0); + CHECK_EQ(ss1, s1); + // + CHECK_NE(s1, ss0); + CHECK_NE(s0, ss1); + CHECK_NE(ss1, s0); + CHECK_NE(ss0, s1); + // + CHECK_GE(s0, ss0); + CHECK_LE(s1, ss1); + CHECK_GE(ss0, s0); + CHECK_LE(ss1, s1); + CHECK_GE(s1, ss0); + CHECK_LE(s0, ss1); + CHECK_GE(ss1, s0); + CHECK_LE(ss0, s1); + // + CHECK_GT(s1, ss0); + CHECK_LT(s0, ss1); + CHECK_GT(ss1, s0); + CHECK_LT(ss0, s1); +} + +TEST_CASE("std_vector.to_chars") +{ + const std::vector<char> s0 = ctor("000"); + char buf_[100] = {}; + substr buf = buf_; + CHECK_NE(buf.data(), s0.data()); + size_t ret = to_chars({}, s0); + CHECK_EQ(ret, s0.size()); + CHECK_NE(buf.first(ret), s0); + ret = to_chars(buf, s0); + CHECK_EQ(ret, s0.size()); + CHECK_EQ(buf.first(ret), s0); +} + +TEST_CASE("std_vector.from_chars") +{ + std::vector<char> s0; + csubstr buf = "0123456798"; + CHECK_NE(buf.data(), s0.data()); + bool ok = from_chars(buf, &s0); + CHECK(ok); + CHECK_EQ(buf, s0); +} + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/test/test_substr.cpp b/thirdparty/ryml/ext/c4core/test/test_substr.cpp new file mode 100644 index 000000000..777b4b4b0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_substr.cpp @@ -0,0 +1,4507 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/std/std.hpp" +#include "c4/substr.hpp" +#endif + +#include <c4/test.hpp> + +#include "c4/libtest/supprwarn_push.hpp" +#include <iostream> + +namespace c4 { + +TEST_CASE("substr.ctor_from_char") +{ + char buf1[] = "{foo: 1}"; + char buf2[] = "{foo: 2}"; + substr s(buf1); + CHECK_EQ(s, "{foo: 1}"); + s = buf2; + CHECK_EQ(s, "{foo: 2}"); +} + +TEST_CASE("csubstr.ctor_from_char") +{ + char buf1[] = "{foo: 1}"; + char buf2[] = "{foo: 2}"; + csubstr s(buf1); + CHECK_EQ(s, "{foo: 1}"); + s = buf2; + CHECK_EQ(s, "{foo: 2}"); +} + +TEST_CASE("csubstr.empty_vs_null") +{ + csubstr s; + CHECK_UNARY(s.empty()); + CHECK_UNARY(s.len == 0); + CHECK_UNARY(s.str == nullptr); + CHECK_UNARY(s == nullptr); + + s = ""; + CHECK_UNARY(s.empty()); + CHECK_UNARY(s.len == 0); + CHECK_UNARY(s.str != nullptr); + CHECK_UNARY(s != nullptr); + + s = nullptr; + CHECK_UNARY(s.empty()); + CHECK_UNARY(s.len == 0); + CHECK_UNARY(s.str == nullptr); + CHECK_UNARY(s == nullptr); + + s = ""; + CHECK_UNARY(s.empty()); + CHECK_UNARY(s.len == 0); + CHECK_UNARY(s.str != nullptr); + CHECK_UNARY(s != nullptr); + + s = {}; + CHECK_UNARY(s.empty()); + CHECK_UNARY(s.len == 0); + CHECK_UNARY(s.str == nullptr); + CHECK_UNARY(s == nullptr); + + csubstr pp(nullptr); + CHECK_UNARY(pp.empty()); + CHECK_UNARY(pp.len == 0); + CHECK_UNARY(pp.str == nullptr); + CHECK_UNARY(pp == nullptr); +} + +TEST_CASE("substr.is_sub") +{ + csubstr buf = "0123456789"; + + // ref + csubstr s; + csubstr ref = buf.select("345"); + CHECK_EQ(ref, "345"); + CHECK_UNARY(buf.is_super(ref)); + CHECK_UNARY(ref.is_sub(buf)); + CHECK_FALSE(ref.is_super(buf)); + CHECK_FALSE(buf.is_sub(ref)); + + buf.clear(); + ref.clear(); + CHECK_FALSE(buf.is_super(ref)); + CHECK_FALSE(ref.is_super(buf)); + CHECK_FALSE(ref.is_sub(buf)); + CHECK_FALSE(buf.is_sub(ref)); + + buf = ""; + ref = buf; + CHECK_FALSE(buf.is_super("a")); + CHECK_UNARY(buf.is_super(ref)); +} + +TEST_CASE("substr.overlaps") +{ + csubstr buf = "0123456789"; + + // ref + csubstr s; + csubstr ref = buf.select("345"); + CHECK_EQ(ref.len, 3); + CHECK_EQ(ref, "345"); + + // all_left + s = buf.sub(0, 2); + CHECK_EQ(s, "01"); + CHECK_FALSE(ref.overlaps(s)); + CHECK_FALSE(s.overlaps(ref)); + + // all_left_tight + s = buf.sub(0, 3); + CHECK_EQ(s, "012"); + CHECK_FALSE(ref.overlaps(s)); + CHECK_FALSE(s.overlaps(ref)); + + // overlap_left + s = buf.sub(0, 4); + CHECK_EQ(s, "0123"); + CHECK_UNARY(ref.overlaps(s)); + CHECK_UNARY(s.overlaps(ref)); + + // inside_tight_left + s = buf.sub(3, 1); + CHECK_EQ(s, "3"); + CHECK_UNARY(ref.overlaps(s)); + CHECK_UNARY(s.overlaps(ref)); + s = buf.sub(3, 2); + CHECK_EQ(s, "34"); + CHECK_UNARY(ref.overlaps(s)); + CHECK_UNARY(s.overlaps(ref)); + + // all_inside_tight + s = buf.sub(4, 1); + CHECK_EQ(s, "4"); + CHECK_UNARY(ref.overlaps(s)); + CHECK_UNARY(s.overlaps(ref)); + s = buf.sub(3, 3); + CHECK_EQ(s, "345"); + CHECK_UNARY(ref.overlaps(s)); + CHECK_UNARY(s.overlaps(ref)); + + // inside_tight_right + s = buf.sub(4, 2); + CHECK_EQ(s, "45"); + CHECK_UNARY(ref.overlaps(s)); + CHECK_UNARY(s.overlaps(ref)); + s = buf.sub(5, 1); + CHECK_EQ(s, "5"); + CHECK_UNARY(ref.overlaps(s)); + CHECK_UNARY(s.overlaps(ref)); + + // overlap_right + s = buf.sub(5, 2); + CHECK_EQ(s, "56"); + CHECK_UNARY(ref.overlaps(s)); + CHECK_UNARY(s.overlaps(ref)); + s = buf.sub(5, 3); + CHECK_EQ(s, "567"); + CHECK_UNARY(ref.overlaps(s)); + CHECK_UNARY(s.overlaps(ref)); + + // all_right_tight + s = buf.sub(6, 1); + CHECK_EQ(s, "6"); + CHECK_FALSE(ref.overlaps(s)); + CHECK_FALSE(s.overlaps(ref)); + s = buf.sub(6, 2); + CHECK_EQ(s, "67"); + CHECK_FALSE(ref.overlaps(s)); + CHECK_FALSE(s.overlaps(ref)); + + // all_right + s = buf.sub(7, 1); + CHECK_EQ(s, "7"); + CHECK_FALSE(ref.overlaps(s)); + CHECK_FALSE(s.overlaps(ref)); + s = buf.sub(7, 2); + CHECK_EQ(s, "78"); + CHECK_FALSE(ref.overlaps(s)); + CHECK_FALSE(s.overlaps(ref)); + + // null vs null + csubstr n1, n2; + CHECK_EQ(n1.str, nullptr); + CHECK_EQ(n2.str, nullptr); + CHECK_EQ(n1.len, 0); + CHECK_EQ(n2.len, 0); + CHECK_FALSE(n1.overlaps(n2)); + CHECK_FALSE(n2.overlaps(n1)); +} + +TEST_CASE("substr.sub") +{ + CHECK_EQ(csubstr("10]").sub(0, 2), "10"); +} + +TEST_CASE("substr.range") +{ + csubstr s = "0123456789"; + CHECK_EQ(s.range(0, 10), "0123456789"); + CHECK_EQ(s.range(0 ), "0123456789"); + CHECK_EQ(s.range(1, 10), "123456789"); + CHECK_EQ(s.range(1 ), "123456789"); + CHECK_EQ(s.range(2, 10), "23456789"); + CHECK_EQ(s.range(2 ), "23456789"); + CHECK_EQ(s.range(3, 10), "3456789"); + CHECK_EQ(s.range(3 ), "3456789"); + CHECK_EQ(s.range(4, 10), "456789"); + CHECK_EQ(s.range(4 ), "456789"); + CHECK_EQ(s.range(5, 10), "56789"); + CHECK_EQ(s.range(5 ), "56789"); + CHECK_EQ(s.range(6, 10), "6789"); + CHECK_EQ(s.range(6 ), "6789"); + CHECK_EQ(s.range(7, 10), "789"); + CHECK_EQ(s.range(7 ), "789"); + CHECK_EQ(s.range(8, 10), "89"); + CHECK_EQ(s.range(8 ), "89"); + CHECK_EQ(s.range(9, 10), "9"); + CHECK_EQ(s.range(9 ), "9"); + CHECK_EQ(s.range(10, 10), ""); + CHECK_EQ(s.range(10 ), ""); + + CHECK_EQ(s.range(0 , 9), "012345678"); + CHECK_EQ(s.range(1 , 9), "12345678"); + CHECK_EQ(s.range(2 , 9), "2345678"); + CHECK_EQ(s.range(3 , 9), "345678"); + CHECK_EQ(s.range(4 , 9), "45678"); + CHECK_EQ(s.range(5 , 9), "5678"); + CHECK_EQ(s.range(6 , 9), "678"); + CHECK_EQ(s.range(7 , 9), "78"); + CHECK_EQ(s.range(8 , 9), "8"); + CHECK_EQ(s.range(9 , 9), ""); + + CHECK_EQ(s.range(0 , 7), "0123456"); + CHECK_EQ(s.range(1 , 7), "123456"); + CHECK_EQ(s.range(2 , 7), "23456"); + CHECK_EQ(s.range(3 , 7), "3456"); + CHECK_EQ(s.range(4 , 7), "456"); + CHECK_EQ(s.range(5 , 7), "56"); + CHECK_EQ(s.range(6 , 7), "6"); + CHECK_EQ(s.range(7 , 7), ""); + + CHECK_EQ(s.range(0 , 5), "01234"); + CHECK_EQ(s.range(1 , 5), "1234"); + CHECK_EQ(s.range(2 , 5), "234"); + CHECK_EQ(s.range(3 , 5), "34"); + CHECK_EQ(s.range(4 , 5), "4"); + CHECK_EQ(s.range(5 , 5), ""); + + CHECK_EQ(s.range(0 , 3), "012"); + CHECK_EQ(s.range(1 , 3), "12"); + CHECK_EQ(s.range(2 , 3), "2"); + CHECK_EQ(s.range(3 , 3), ""); + + CHECK_EQ(s.range(0 , 2), "01"); + CHECK_EQ(s.range(1 , 2), "1"); + CHECK_EQ(s.range(2 , 2), ""); + + CHECK_EQ(s.range(0 , 1), "0"); + CHECK_EQ(s.range(1 , 1), ""); +} + +TEST_CASE("substr.first") +{ + csubstr s = "0123456789"; + + CHECK_EQ(s.first(csubstr::npos), s); + + CHECK_EQ(s.first(10), "0123456789"); + CHECK_EQ(s.first(9), "012345678"); + CHECK_EQ(s.first(8), "01234567"); + CHECK_EQ(s.first(7), "0123456"); + CHECK_EQ(s.first(6), "012345"); + CHECK_EQ(s.first(5), "01234"); + CHECK_EQ(s.first(4), "0123"); + CHECK_EQ(s.first(3), "012"); + CHECK_EQ(s.first(2), "01"); + CHECK_EQ(s.first(1), "0"); + CHECK_EQ(s.first(0), ""); +} + +TEST_CASE("substr.last") +{ + csubstr s = "0123456789"; + + CHECK_EQ(s.last(csubstr::npos), s); + + CHECK_EQ(s.last(10), "0123456789"); + CHECK_EQ(s.last(9), "123456789"); + CHECK_EQ(s.last(8), "23456789"); + CHECK_EQ(s.last(7), "3456789"); + CHECK_EQ(s.last(6), "456789"); + CHECK_EQ(s.last(5), "56789"); + CHECK_EQ(s.last(4), "6789"); + CHECK_EQ(s.last(3), "789"); + CHECK_EQ(s.last(2), "89"); + CHECK_EQ(s.last(1), "9"); + CHECK_EQ(s.last(0), ""); +} + +TEST_CASE("substr.offs") +{ + csubstr s = "0123456789"; + + CHECK_EQ(s.offs(0, 0), s); + + CHECK_EQ(s.offs(1, 0), "123456789"); + CHECK_EQ(s.offs(0, 1), "012345678"); + CHECK_EQ(s.offs(1, 1), "12345678"); + + CHECK_EQ(s.offs(1, 2), "1234567"); + CHECK_EQ(s.offs(2, 1), "2345678"); + CHECK_EQ(s.offs(2, 2), "234567"); + + CHECK_EQ(s.offs(2, 3), "23456"); + CHECK_EQ(s.offs(3, 2), "34567"); + CHECK_EQ(s.offs(3, 3), "3456"); + + CHECK_EQ(s.offs(3, 4), "345"); + CHECK_EQ(s.offs(4, 3), "456"); + CHECK_EQ(s.offs(4, 4), "45"); + + CHECK_EQ(s.offs(4, 5), "4"); + CHECK_EQ(s.offs(5, 4), "5"); + CHECK_EQ(s.offs(5, 5), ""); +} + +TEST_CASE("substr.count") +{ + csubstr buf = "0123456789"; + + CHECK_EQ(buf.count('0'), 1); + CHECK_EQ(buf.count('0', 0), 1); + CHECK_EQ(buf.count('0', 1), 0); + CHECK_EQ(buf.count('0', buf.len), 0); + + CHECK_EQ(buf.count("01"), 1); + CHECK_EQ(buf.count("01", 0), 1); + CHECK_EQ(buf.count("01", 1), 0); + CHECK_EQ(buf.count("01", buf.len), 0); + + CHECK_EQ(buf.count('1'), 1); + CHECK_EQ(buf.count('1', 0), 1); + CHECK_EQ(buf.count('1', 1), 1); + CHECK_EQ(buf.count('1', 2), 0); + CHECK_EQ(buf.count('1', buf.len), 0); + + CHECK_EQ(buf.count("12"), 1); + CHECK_EQ(buf.count("12", 0), 1); + CHECK_EQ(buf.count("12", 1), 1); + CHECK_EQ(buf.count("12", 2), 0); + CHECK_EQ(buf.count("12", buf.len), 0); + + CHECK_EQ(buf.count('2'), 1); + CHECK_EQ(buf.count('2', 0), 1); + CHECK_EQ(buf.count('2', 1), 1); + CHECK_EQ(buf.count('2', 2), 1); + CHECK_EQ(buf.count('2', 3), 0); + CHECK_EQ(buf.count('2', buf.len), 0); + + CHECK_EQ(buf.count("23"), 1); + CHECK_EQ(buf.count("23", 0), 1); + CHECK_EQ(buf.count("23", 1), 1); + CHECK_EQ(buf.count("23", 2), 1); + CHECK_EQ(buf.count("23", 3), 0); + CHECK_EQ(buf.count("23", buf.len), 0); + + CHECK_EQ(buf.count('3'), 1); + CHECK_EQ(buf.count('3', 0), 1); + CHECK_EQ(buf.count('3', 1), 1); + CHECK_EQ(buf.count('3', 2), 1); + CHECK_EQ(buf.count('3', 3), 1); + CHECK_EQ(buf.count('3', 4), 0); + CHECK_EQ(buf.count('3', buf.len), 0); + + CHECK_EQ(buf.count("34"), 1); + CHECK_EQ(buf.count("34", 0), 1); + CHECK_EQ(buf.count("34", 1), 1); + CHECK_EQ(buf.count("34", 2), 1); + CHECK_EQ(buf.count("34", 3), 1); + CHECK_EQ(buf.count("34", 4), 0); + CHECK_EQ(buf.count("34", buf.len), 0); + + CHECK_EQ(buf.count('4'), 1); + CHECK_EQ(buf.count('4', 0), 1); + CHECK_EQ(buf.count('4', 1), 1); + CHECK_EQ(buf.count('4', 2), 1); + CHECK_EQ(buf.count('4', 3), 1); + CHECK_EQ(buf.count('4', 4), 1); + CHECK_EQ(buf.count('4', 5), 0); + CHECK_EQ(buf.count('4', buf.len), 0); + + CHECK_EQ(buf.count("45"), 1); + CHECK_EQ(buf.count("45", 0), 1); + CHECK_EQ(buf.count("45", 1), 1); + CHECK_EQ(buf.count("45", 2), 1); + CHECK_EQ(buf.count("45", 3), 1); + CHECK_EQ(buf.count("45", 4), 1); + CHECK_EQ(buf.count("45", 5), 0); + CHECK_EQ(buf.count("45", buf.len), 0); + + CHECK_EQ(buf.count('5'), 1); + CHECK_EQ(buf.count('5', 0), 1); + CHECK_EQ(buf.count('5', 1), 1); + CHECK_EQ(buf.count('5', 2), 1); + CHECK_EQ(buf.count('5', 3), 1); + CHECK_EQ(buf.count('5', 4), 1); + CHECK_EQ(buf.count('5', 5), 1); + CHECK_EQ(buf.count('5', 6), 0); + CHECK_EQ(buf.count('5', buf.len), 0); + + CHECK_EQ(buf.count("56"), 1); + CHECK_EQ(buf.count("56", 0), 1); + CHECK_EQ(buf.count("56", 1), 1); + CHECK_EQ(buf.count("56", 2), 1); + CHECK_EQ(buf.count("56", 3), 1); + CHECK_EQ(buf.count("56", 4), 1); + CHECK_EQ(buf.count("56", 5), 1); + CHECK_EQ(buf.count("56", 6), 0); + CHECK_EQ(buf.count("56", buf.len), 0); + + CHECK_EQ(buf.count('a'), 0); + CHECK_EQ(buf.count('a', 0), 0); + CHECK_EQ(buf.count('a', 1), 0); + CHECK_EQ(buf.count('a', 2), 0); + CHECK_EQ(buf.count('a', 3), 0); + CHECK_EQ(buf.count('a', 4), 0); + CHECK_EQ(buf.count('a', 5), 0); + CHECK_EQ(buf.count('a', 6), 0); + CHECK_EQ(buf.count('a', buf.len), 0); + + CHECK_EQ(buf.count("ab"), 0); + CHECK_EQ(buf.count("ab", 0), 0); + CHECK_EQ(buf.count("ab", 1), 0); + CHECK_EQ(buf.count("ab", 2), 0); + CHECK_EQ(buf.count("ab", 3), 0); + CHECK_EQ(buf.count("ab", 4), 0); + CHECK_EQ(buf.count("ab", 5), 0); + CHECK_EQ(buf.count("ab", 6), 0); + CHECK_EQ(buf.count("ab", buf.len), 0); + + buf = "00110022003300440055"; + CHECK_EQ(buf.count('0', 0), 10); + CHECK_EQ(buf.count('0', 1), 9); + CHECK_EQ(buf.count('0', 2), 8); + CHECK_EQ(buf.count('0', 3), 8); + CHECK_EQ(buf.count('0', 4), 8); + CHECK_EQ(buf.count('0', 5), 7); + CHECK_EQ(buf.count('0', 6), 6); + CHECK_EQ(buf.count('0', 7), 6); + CHECK_EQ(buf.count('0', 8), 6); + CHECK_EQ(buf.count('0', 9), 5); + CHECK_EQ(buf.count('0', 10), 4); + CHECK_EQ(buf.count('0', 11), 4); + CHECK_EQ(buf.count('0', 12), 4); + CHECK_EQ(buf.count('0', 13), 3); + CHECK_EQ(buf.count('0', 14), 2); + CHECK_EQ(buf.count('0', 15), 2); + CHECK_EQ(buf.count('0', 16), 2); + CHECK_EQ(buf.count('0', 17), 1); + CHECK_EQ(buf.count('0', 18), 0); + CHECK_EQ(buf.count('0', 19), 0); + CHECK_EQ(buf.count('0', 20), 0); + + CHECK_EQ(buf.count('1', 0), 2); + CHECK_EQ(buf.count('1', 1), 2); + CHECK_EQ(buf.count('1', 2), 2); + CHECK_EQ(buf.count('1', 3), 1); + CHECK_EQ(buf.count('1', 4), 0); + CHECK_EQ(buf.count('1', 5), 0); + + CHECK_EQ(buf.count("01" ), 1); + CHECK_EQ(buf.count("01", 2), 0); + CHECK_EQ(buf.count("10" ), 1); + CHECK_EQ(buf.count("10", 4), 0); + CHECK_EQ(buf.count("00", 0), 5); + CHECK_EQ(buf.count("00", 1), 4); + CHECK_EQ(buf.count("00", 2), 4); + CHECK_EQ(buf.count("00", 3), 4); + CHECK_EQ(buf.count("00", 4), 4); + CHECK_EQ(buf.count("00", 5), 3); + CHECK_EQ(buf.count("00", 6), 3); + CHECK_EQ(buf.count("00", 7), 3); + CHECK_EQ(buf.count("00", 8), 3); + CHECK_EQ(buf.count("00", 9), 2); + CHECK_EQ(buf.count("00", 10), 2); + CHECK_EQ(buf.count("00", 11), 2); + CHECK_EQ(buf.count("00", 12), 2); + CHECK_EQ(buf.count("00", 13), 1); + CHECK_EQ(buf.count("00", 14), 1); + CHECK_EQ(buf.count("00", 15), 1); + CHECK_EQ(buf.count("00", 16), 1); + CHECK_EQ(buf.count("00", 17), 0); + CHECK_EQ(buf.count("00", 18), 0); + CHECK_EQ(buf.count("00", 19), 0); + CHECK_EQ(buf.count("00", 20), 0); +} + +TEST_CASE("substr.select") +{ + csubstr buf = "0123456789"; + + CHECK_EQ(buf.select('0'), "0"); + CHECK_EQ(buf.select('1'), "1"); + CHECK_EQ(buf.select('2'), "2"); + CHECK_EQ(buf.select('8'), "8"); + CHECK_EQ(buf.select('9'), "9"); + + CHECK_EQ(buf.select('a').str, nullptr); + CHECK_EQ(buf.select('a').len, 0); + CHECK_EQ(buf.select('a'), ""); + + CHECK_EQ(buf.select("a").str, nullptr); + CHECK_EQ(buf.select("a").len, 0); + CHECK_EQ(buf.select("a"), ""); + + CHECK_EQ(buf.select("0"), "0"); + CHECK_EQ(buf.select("0").str, buf.str+0); + CHECK_EQ(buf.select("0").len, 1); + + CHECK_EQ(buf.select("1"), "1"); + CHECK_EQ(buf.select("1").str, buf.str+1); + CHECK_EQ(buf.select("1").len, 1); + + CHECK_EQ(buf.select("2"), "2"); + CHECK_EQ(buf.select("2").str, buf.str+2); + CHECK_EQ(buf.select("2").len, 1); + + CHECK_EQ(buf.select("9"), "9"); + CHECK_EQ(buf.select("9").str, buf.str+9); + CHECK_EQ(buf.select("9").len, 1); + + CHECK_EQ(buf.select("012"), "012"); + CHECK_EQ(buf.select("012").str, buf.str+0); + CHECK_EQ(buf.select("012").len, 3); + + CHECK_EQ(buf.select("345"), "345"); + CHECK_EQ(buf.select("345").str, buf.str+3); + CHECK_EQ(buf.select("345").len, 3); + + CHECK_EQ(buf.select("789"), "789"); + CHECK_EQ(buf.select("789").str, buf.str+7); + CHECK_EQ(buf.select("789").len, 3); + + CHECK_EQ(buf.select("89a"), ""); + CHECK_EQ(buf.select("89a").str, nullptr); + CHECK_EQ(buf.select("89a").len, 0); +} + +TEST_CASE("substr.begins_with") +{ + CHECK (csubstr(": ").begins_with(":" )); + CHECK (csubstr(": ").begins_with(':' )); + CHECK_FALSE(csubstr(":") .begins_with(": ")); + + CHECK (csubstr( "1234").begins_with('0', 0)); + CHECK (csubstr( "01234").begins_with('0', 1)); + CHECK_FALSE(csubstr( "01234").begins_with('0', 2)); + CHECK (csubstr( "001234").begins_with('0', 1)); + CHECK (csubstr( "001234").begins_with('0', 2)); + CHECK_FALSE(csubstr( "001234").begins_with('0', 3)); + CHECK (csubstr( "0001234").begins_with('0', 1)); + CHECK (csubstr( "0001234").begins_with('0', 2)); + CHECK (csubstr( "0001234").begins_with('0', 3)); + CHECK_FALSE(csubstr( "0001234").begins_with('0', 4)); + CHECK (csubstr("00001234").begins_with('0', 1)); + CHECK (csubstr("00001234").begins_with('0', 2)); + CHECK (csubstr("00001234").begins_with('0', 3)); + CHECK (csubstr("00001234").begins_with('0', 4)); + CHECK_FALSE(csubstr("00001234").begins_with('0', 5)); +} + +TEST_CASE("substr.ends_with") +{ + CHECK_UNARY(csubstr("{% if foo %}bar{% endif %}").ends_with("{% endif %}")); + + CHECK (csubstr("1234" ).ends_with('0', 0)); + CHECK (csubstr("12340" ).ends_with('0', 1)); + CHECK_FALSE(csubstr("12340" ).ends_with('0', 2)); + CHECK (csubstr("123400" ).ends_with('0', 1)); + CHECK (csubstr("123400" ).ends_with('0', 2)); + CHECK_FALSE(csubstr("123400" ).ends_with('0', 3)); + CHECK (csubstr("1234000" ).ends_with('0', 1)); + CHECK (csubstr("1234000" ).ends_with('0', 2)); + CHECK (csubstr("1234000" ).ends_with('0', 3)); + CHECK_FALSE(csubstr("1234000" ).ends_with('0', 4)); + CHECK (csubstr("12340000").ends_with('0', 1)); + CHECK (csubstr("12340000").ends_with('0', 2)); + CHECK (csubstr("12340000").ends_with('0', 3)); + CHECK (csubstr("12340000").ends_with('0', 4)); + CHECK_FALSE(csubstr("12340000").ends_with('0', 5)); +} + +TEST_CASE("substr.find") +{ + csubstr s012345 = "012345"; + CHECK(s012345.find('a') == csubstr::npos); + CHECK(s012345.find('0' ) == 0u); + CHECK(s012345.find('0', 1u) == csubstr::npos); + CHECK(s012345.find('1' ) == 1u); + CHECK(s012345.find('1', 2u) == csubstr::npos); + CHECK(s012345.find('2' ) == 2u); + CHECK(s012345.find('2', 3u) == csubstr::npos); + CHECK(s012345.find('3' ) == 3u); + CHECK(s012345.find('3', 4u) == csubstr::npos); + CHECK(s012345.find("ab" ) == csubstr::npos); + CHECK(s012345.find("01" ) == 0u); + CHECK(s012345.find("01", 1u) == csubstr::npos); + CHECK(s012345.find("12" ) == 1u); + CHECK(s012345.find("12", 2u) == csubstr::npos); + CHECK(s012345.find("23" ) == 2u); + CHECK(s012345.find("23", 3u) == csubstr::npos); +} + +TEST_CASE("substr.first_of") +{ + size_t npos = csubstr::npos; + + CHECK_EQ(csubstr("012345").first_of('a'), npos); + CHECK_EQ(csubstr("012345").first_of("ab"), npos); + + CHECK_EQ(csubstr("012345").first_of('0'), 0u); + CHECK_EQ(csubstr("012345").first_of("0"), 0u); + CHECK_EQ(csubstr("012345").first_of("01"), 0u); + CHECK_EQ(csubstr("012345").first_of("10"), 0u); + CHECK_EQ(csubstr("012345").first_of("012"), 0u); + CHECK_EQ(csubstr("012345").first_of("210"), 0u); + CHECK_EQ(csubstr("012345").first_of("0123"), 0u); + CHECK_EQ(csubstr("012345").first_of("3210"), 0u); + CHECK_EQ(csubstr("012345").first_of("01234"), 0u); + CHECK_EQ(csubstr("012345").first_of("43210"), 0u); + CHECK_EQ(csubstr("012345").first_of("012345"), 0u); + CHECK_EQ(csubstr("012345").first_of("543210"), 0u); + + CHECK_EQ(csubstr("012345").first_of('0', 2u), npos); + CHECK_EQ(csubstr("012345").first_of("0", 2u), npos); + CHECK_EQ(csubstr("012345").first_of("01", 2u), npos); + CHECK_EQ(csubstr("012345").first_of("10", 2u), npos); + CHECK_EQ(csubstr("012345").first_of("012", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("210", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("0123", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("3210", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("01234", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("43210", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("012345", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("543210", 2u), 2u); + + CHECK_EQ(csubstr("012345").first_of('5'), 5u); + CHECK_EQ(csubstr("012345").first_of("5"), 5u); + CHECK_EQ(csubstr("012345").first_of("45"), 4u); + CHECK_EQ(csubstr("012345").first_of("54"), 4u); + CHECK_EQ(csubstr("012345").first_of("345"), 3u); + CHECK_EQ(csubstr("012345").first_of("543"), 3u); + CHECK_EQ(csubstr("012345").first_of("2345"), 2u); + CHECK_EQ(csubstr("012345").first_of("5432"), 2u); + CHECK_EQ(csubstr("012345").first_of("12345"), 1u); + CHECK_EQ(csubstr("012345").first_of("54321"), 1u); + CHECK_EQ(csubstr("012345").first_of("012345"), 0u); + CHECK_EQ(csubstr("012345").first_of("543210"), 0u); + + CHECK_EQ(csubstr("012345").first_of('5', 2u), 5u); + CHECK_EQ(csubstr("012345").first_of("5", 2u), 5u); + CHECK_EQ(csubstr("012345").first_of("45", 2u), 4u); + CHECK_EQ(csubstr("012345").first_of("54", 2u), 4u); + CHECK_EQ(csubstr("012345").first_of("345", 2u), 3u); + CHECK_EQ(csubstr("012345").first_of("543", 2u), 3u); + CHECK_EQ(csubstr("012345").first_of("2345", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("5432", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("12345", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("54321", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("012345", 2u), 2u); + CHECK_EQ(csubstr("012345").first_of("543210", 2u), 2u); + + CHECK_EQ(csubstr{}.first_of('0'), npos); + CHECK_EQ(csubstr{}.first_of('0', 0u), npos); + CHECK_EQ(csubstr("012345").first_of('0', 6u), npos); + CHECK_EQ(csubstr("012345").first_of('5', 6u), npos); + CHECK_EQ(csubstr("012345").first_of("012345", 6u), npos); +} + +TEST_CASE("substr.last_of") +{ + size_t npos = csubstr::npos; + + CHECK_EQ(csubstr("012345").last_of('a'), npos); + CHECK_EQ(csubstr("012345").last_of("ab"), npos); + + CHECK_EQ(csubstr("012345").last_of('0'), 0u); + CHECK_EQ(csubstr("012345").last_of("0"), 0u); + CHECK_EQ(csubstr("012345").last_of("01"), 1u); + CHECK_EQ(csubstr("012345").last_of("10"), 1u); + CHECK_EQ(csubstr("012345").last_of("012"), 2u); + CHECK_EQ(csubstr("012345").last_of("210"), 2u); + CHECK_EQ(csubstr("012345").last_of("0123"), 3u); + CHECK_EQ(csubstr("012345").last_of("3210"), 3u); + CHECK_EQ(csubstr("012345").last_of("01234"), 4u); + CHECK_EQ(csubstr("012345").last_of("43210"), 4u); + CHECK_EQ(csubstr("012345").last_of("012345"), 5u); + CHECK_EQ(csubstr("012345").last_of("543210"), 5u); + + CHECK_EQ(csubstr("012345").last_of('0', 2u), 0u); + CHECK_EQ(csubstr("012345").last_of("0", 2u), 0u); + CHECK_EQ(csubstr("012345").last_of("01", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("10", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("012", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("210", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("0123", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("3210", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("01234", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("43210", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("012345", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("543210", 2u), 1u); + + CHECK_EQ(csubstr("012345").last_of('5'), 5u); + CHECK_EQ(csubstr("012345").last_of("5"), 5u); + CHECK_EQ(csubstr("012345").last_of("45"), 5u); + CHECK_EQ(csubstr("012345").last_of("54"), 5u); + CHECK_EQ(csubstr("012345").last_of("345"), 5u); + CHECK_EQ(csubstr("012345").last_of("543"), 5u); + CHECK_EQ(csubstr("012345").last_of("2345"), 5u); + CHECK_EQ(csubstr("012345").last_of("5432"), 5u); + CHECK_EQ(csubstr("012345").last_of("12345"), 5u); + CHECK_EQ(csubstr("012345").last_of("54321"), 5u); + CHECK_EQ(csubstr("012345").last_of("012345"), 5u); + CHECK_EQ(csubstr("012345").last_of("543210"), 5u); + + CHECK_EQ(csubstr("012345").last_of('5', 2u), npos); + CHECK_EQ(csubstr("012345").last_of("5", 2u), npos); + CHECK_EQ(csubstr("012345").last_of("45", 2u), npos); + CHECK_EQ(csubstr("012345").last_of("54", 2u), npos); + CHECK_EQ(csubstr("012345").last_of("345", 2u), npos); + CHECK_EQ(csubstr("012345").last_of("543", 2u), npos); + CHECK_EQ(csubstr("012345").last_of("2345", 2u), npos); + CHECK_EQ(csubstr("012345").last_of("5432", 2u), npos); + CHECK_EQ(csubstr("012345").last_of("12345", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("54321", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("012345", 2u), 1u); + CHECK_EQ(csubstr("012345").last_of("543210", 2u), 1u); + + CHECK_EQ(csubstr{}.last_of('?'), npos); + CHECK_EQ(csubstr("012345").last_of('0', 6u), 0u); + CHECK_EQ(csubstr("012345").last_of('5', 6u), 5u); + CHECK_EQ(csubstr("012345").last_of("012345", 6u), 5u); +} + +TEST_CASE("substr.first_not_of") +{ + size_t npos = csubstr::npos; + + CHECK_EQ(csubstr("012345").first_not_of('a'), 0u); + CHECK_EQ(csubstr("012345").first_not_of("ab"), 0u); + + CHECK_EQ(csubstr("012345").first_not_of('0'), 1u); + CHECK_EQ(csubstr("012345").first_not_of("0"), 1u); + CHECK_EQ(csubstr("012345").first_not_of("01"), 2u); + CHECK_EQ(csubstr("012345").first_not_of("10"), 2u); + CHECK_EQ(csubstr("012345").first_not_of("012"), 3u); + CHECK_EQ(csubstr("012345").first_not_of("210"), 3u); + CHECK_EQ(csubstr("012345").first_not_of("0123"), 4u); + CHECK_EQ(csubstr("012345").first_not_of("3210"), 4u); + CHECK_EQ(csubstr("012345").first_not_of("01234"), 5u); + CHECK_EQ(csubstr("012345").first_not_of("43210"), 5u); + CHECK_EQ(csubstr("012345").first_not_of("012345"), npos); + CHECK_EQ(csubstr("012345").first_not_of("543210"), npos); + + CHECK_EQ(csubstr("012345").first_not_of('0', 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("0", 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("01", 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("10", 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("012", 2u), 3u); + CHECK_EQ(csubstr("012345").first_not_of("210", 2u), 3u); + CHECK_EQ(csubstr("012345").first_not_of("0123", 2u), 4u); + CHECK_EQ(csubstr("012345").first_not_of("3210", 2u), 4u); + CHECK_EQ(csubstr("012345").first_not_of("01234", 2u), 5u); + CHECK_EQ(csubstr("012345").first_not_of("43210", 2u), 5u); + CHECK_EQ(csubstr("012345").first_not_of("012345", 2u), npos); + CHECK_EQ(csubstr("012345").first_not_of("543210", 2u), npos); + + CHECK_EQ(csubstr("012345").first_not_of('5'), 0u); + CHECK_EQ(csubstr("012345").first_not_of("5"), 0u); + CHECK_EQ(csubstr("012345").first_not_of("45"), 0u); + CHECK_EQ(csubstr("012345").first_not_of("54"), 0u); + CHECK_EQ(csubstr("012345").first_not_of("345"), 0u); + CHECK_EQ(csubstr("012345").first_not_of("543"), 0u); + CHECK_EQ(csubstr("012345").first_not_of("2345"), 0u); + CHECK_EQ(csubstr("012345").first_not_of("5432"), 0u); + CHECK_EQ(csubstr("012345").first_not_of("12345"), 0u); + CHECK_EQ(csubstr("012345").first_not_of("54321"), 0u); + CHECK_EQ(csubstr("012345").first_not_of("012345"), npos); + CHECK_EQ(csubstr("012345").first_not_of("543210"), npos); + + CHECK_EQ(csubstr("012345").first_not_of('5', 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("5", 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("45", 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("54", 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("345", 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("543", 2u), 2u); + CHECK_EQ(csubstr("012345").first_not_of("2345", 2u), npos); + CHECK_EQ(csubstr("012345").first_not_of("5432", 2u), npos); + CHECK_EQ(csubstr("012345").first_not_of("12345", 2u), npos); + CHECK_EQ(csubstr("012345").first_not_of("54321", 2u), npos); + CHECK_EQ(csubstr("012345").first_not_of("012345", 2u), npos); + CHECK_EQ(csubstr("012345").first_not_of("543210", 2u), npos); + + CHECK_EQ(csubstr("").first_not_of('0', 0u), npos); + CHECK_EQ(csubstr("012345").first_not_of('5', 6u), npos); + CHECK_EQ(csubstr("012345").first_not_of("012345", 6u), npos); +} + +TEST_CASE("substr.last_not_of") +{ + size_t npos = csubstr::npos; + + CHECK_EQ(csubstr("012345").last_not_of('a'), 5u); + CHECK_EQ(csubstr("012345").last_not_of("ab"), 5u); + + CHECK_EQ(csubstr("012345").last_not_of('5'), 4u); + CHECK_EQ(csubstr("012345").last_not_of("5"), 4u); + CHECK_EQ(csubstr("012345").last_not_of("45"), 3u); + CHECK_EQ(csubstr("012345").last_not_of("54"), 3u); + CHECK_EQ(csubstr("012345").last_not_of("345"), 2u); + CHECK_EQ(csubstr("012345").last_not_of("543"), 2u); + CHECK_EQ(csubstr("012345").last_not_of("2345"), 1u); + CHECK_EQ(csubstr("012345").last_not_of("5432"), 1u); + CHECK_EQ(csubstr("012345").last_not_of("12345"), 0u); + CHECK_EQ(csubstr("012345").last_not_of("54321"), 0u); + CHECK_EQ(csubstr("012345").last_not_of("012345"), npos); + CHECK_EQ(csubstr("012345").last_not_of("543210"), npos); + + CHECK_EQ(csubstr("012345").last_not_of('5', 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("5", 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("45", 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("54", 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("345", 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("543", 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("2345", 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("5432", 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("12345", 2u), 0u); + CHECK_EQ(csubstr("012345").last_not_of("54321", 2u), 0u); + CHECK_EQ(csubstr("012345").last_not_of("012345", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("543210", 2u), npos); + + CHECK_EQ(csubstr("012345").last_not_of('0'), 5u); + CHECK_EQ(csubstr("012345").last_not_of("0"), 5u); + CHECK_EQ(csubstr("012345").last_not_of("01"), 5u); + CHECK_EQ(csubstr("012345").last_not_of("10"), 5u); + CHECK_EQ(csubstr("012345").last_not_of("012"), 5u); + CHECK_EQ(csubstr("012345").last_not_of("210"), 5u); + CHECK_EQ(csubstr("012345").last_not_of("0123"), 5u); + CHECK_EQ(csubstr("012345").last_not_of("3210"), 5u); + CHECK_EQ(csubstr("012345").last_not_of("01234"), 5u); + CHECK_EQ(csubstr("012345").last_not_of("43210"), 5u); + CHECK_EQ(csubstr("012345").last_not_of("012345"), npos); + CHECK_EQ(csubstr("012345").last_not_of("543210"), npos); + + CHECK_EQ(csubstr("012345").last_not_of('0', 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("0", 2u), 1u); + CHECK_EQ(csubstr("012345").last_not_of("01", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("10", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("012", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("210", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("0123", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("3210", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("01234", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("43210", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("012345", 2u), npos); + CHECK_EQ(csubstr("012345").last_not_of("543210", 2u), npos); + + CHECK_EQ(csubstr("").last_not_of('0', 0u), npos); + CHECK_EQ(csubstr("012345").last_not_of('5', 6u), 4u); +} + +TEST_CASE("substr.left_of") +{ + csubstr s = "012345"; + + CHECK_EQ(s.left_of(csubstr::npos), s); + CHECK_EQ(s.left_of(csubstr::npos, /*include_pos*/false), s); + CHECK_EQ(s.left_of(csubstr::npos, /*include_pos*/true), s); + + + CHECK_EQ(s.left_of(0), ""); + CHECK_EQ(s.left_of(1), "0"); + CHECK_EQ(s.left_of(2), "01"); + CHECK_EQ(s.left_of(3), "012"); + CHECK_EQ(s.left_of(4), "0123"); + CHECK_EQ(s.left_of(5), "01234"); + CHECK_EQ(s.left_of(6), "012345"); + + CHECK_EQ(s.left_of(0, /*include_pos*/false), ""); + CHECK_EQ(s.left_of(1, /*include_pos*/false), "0"); + CHECK_EQ(s.left_of(2, /*include_pos*/false), "01"); + CHECK_EQ(s.left_of(3, /*include_pos*/false), "012"); + CHECK_EQ(s.left_of(4, /*include_pos*/false), "0123"); + CHECK_EQ(s.left_of(5, /*include_pos*/false), "01234"); + CHECK_EQ(s.left_of(6, /*include_pos*/false), "012345"); + + CHECK_UNARY(s.is_super(s.left_of(0, /*include_pos*/false))); + CHECK_UNARY(s.is_super(s.left_of(1, /*include_pos*/false))); + CHECK_UNARY(s.is_super(s.left_of(2, /*include_pos*/false))); + CHECK_UNARY(s.is_super(s.left_of(3, /*include_pos*/false))); + CHECK_UNARY(s.is_super(s.left_of(4, /*include_pos*/false))); + + + CHECK_EQ(s.left_of(0, /*include_pos*/true), "0"); + CHECK_EQ(s.left_of(1, /*include_pos*/true), "01"); + CHECK_EQ(s.left_of(2, /*include_pos*/true), "012"); + CHECK_EQ(s.left_of(3, /*include_pos*/true), "0123"); + CHECK_EQ(s.left_of(4, /*include_pos*/true), "01234"); + CHECK_EQ(s.left_of(5, /*include_pos*/true), "012345"); + + CHECK_UNARY(s.is_super(s.left_of(0, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.left_of(1, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.left_of(2, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.left_of(3, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.left_of(4, /*include_pos*/true))); + + + CHECK_EQ(s.sub(5), "5"); + CHECK_EQ(s.sub(4), "45"); + CHECK_EQ(s.sub(3), "345"); + CHECK_EQ(s.sub(2), "2345"); + CHECK_EQ(s.sub(1), "12345"); + CHECK_EQ(s.sub(0), "012345"); + + CHECK_EQ(s.left_of(s.sub(5)), "01234"); + CHECK_EQ(s.left_of(s.sub(4)), "0123"); + CHECK_EQ(s.left_of(s.sub(3)), "012"); + CHECK_EQ(s.left_of(s.sub(2)), "01"); + CHECK_EQ(s.left_of(s.sub(1)), "0"); + CHECK_EQ(s.left_of(s.sub(0)), ""); + + CHECK_UNARY(s.is_super(s.left_of(s.sub(5)))); + CHECK_UNARY(s.is_super(s.left_of(s.sub(4)))); + CHECK_UNARY(s.is_super(s.left_of(s.sub(3)))); + CHECK_UNARY(s.is_super(s.left_of(s.sub(2)))); + CHECK_UNARY(s.is_super(s.left_of(s.sub(1)))); + CHECK_UNARY(s.is_super(s.left_of(s.sub(0)))); +} + +TEST_CASE("substr.right_of") +{ + csubstr s = "012345"; + + CHECK_EQ(s.right_of(csubstr::npos), ""); + CHECK_EQ(s.right_of(csubstr::npos), ""); + + CHECK_EQ(s.right_of(csubstr::npos, /*include_pos*/false), ""); + CHECK_EQ(s.right_of(csubstr::npos, /*include_pos*/true), ""); + + + CHECK_EQ(s.right_of(0), "12345"); + CHECK_EQ(s.right_of(1), "2345"); + CHECK_EQ(s.right_of(2), "345"); + CHECK_EQ(s.right_of(3), "45"); + CHECK_EQ(s.right_of(4), "5"); + CHECK_EQ(s.right_of(5), ""); + + CHECK_EQ(s.right_of(0, /*include_pos*/false), "12345"); + CHECK_EQ(s.right_of(1, /*include_pos*/false), "2345"); + CHECK_EQ(s.right_of(2, /*include_pos*/false), "345"); + CHECK_EQ(s.right_of(3, /*include_pos*/false), "45"); + CHECK_EQ(s.right_of(4, /*include_pos*/false), "5"); + CHECK_EQ(s.right_of(5, /*include_pos*/false), ""); + + CHECK_UNARY(s.is_super(s.right_of(0))); + CHECK_UNARY(s.is_super(s.right_of(1))); + CHECK_UNARY(s.is_super(s.right_of(2))); + CHECK_UNARY(s.is_super(s.right_of(3))); + CHECK_UNARY(s.is_super(s.right_of(4))); + CHECK_UNARY(s.is_super(s.right_of(5))); + + CHECK_UNARY(s.is_super(s.right_of(0, /*include_pos*/false))); + CHECK_UNARY(s.is_super(s.right_of(1, /*include_pos*/false))); + CHECK_UNARY(s.is_super(s.right_of(2, /*include_pos*/false))); + CHECK_UNARY(s.is_super(s.right_of(3, /*include_pos*/false))); + CHECK_UNARY(s.is_super(s.right_of(4, /*include_pos*/false))); + CHECK_UNARY(s.is_super(s.right_of(5, /*include_pos*/false))); + + + CHECK_EQ(s.right_of(0, /*include_pos*/true), "012345"); + CHECK_EQ(s.right_of(1, /*include_pos*/true), "12345"); + CHECK_EQ(s.right_of(2, /*include_pos*/true), "2345"); + CHECK_EQ(s.right_of(3, /*include_pos*/true), "345"); + CHECK_EQ(s.right_of(4, /*include_pos*/true), "45"); + CHECK_EQ(s.right_of(5, /*include_pos*/true), "5"); + CHECK_EQ(s.right_of(6, /*include_pos*/true), ""); + + CHECK_UNARY(s.is_super(s.right_of(0, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.right_of(1, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.right_of(2, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.right_of(3, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.right_of(4, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.right_of(5, /*include_pos*/true))); + CHECK_UNARY(s.is_super(s.right_of(6, /*include_pos*/true))); + + + CHECK_EQ(s.sub(0, 0), ""); + CHECK_EQ(s.sub(0, 1), "0"); + CHECK_EQ(s.sub(0, 2), "01"); + CHECK_EQ(s.sub(0, 3), "012"); + CHECK_EQ(s.sub(0, 4), "0123"); + CHECK_EQ(s.sub(0, 5), "01234"); + CHECK_EQ(s.sub(0, 6), "012345"); + + CHECK_EQ(s.right_of(s.sub(0, 0)), "012345"); + CHECK_EQ(s.right_of(s.sub(0, 1)), "12345"); + CHECK_EQ(s.right_of(s.sub(0, 2)), "2345"); + CHECK_EQ(s.right_of(s.sub(0, 3)), "345"); + CHECK_EQ(s.right_of(s.sub(0, 4)), "45"); + CHECK_EQ(s.right_of(s.sub(0, 5)), "5"); + CHECK_EQ(s.right_of(s.sub(0, 6)), ""); + + CHECK_UNARY(s.is_super(s.right_of(s.sub(0, 0)))); + CHECK_UNARY(s.is_super(s.right_of(s.sub(0, 1)))); + CHECK_UNARY(s.is_super(s.right_of(s.sub(0, 2)))); + CHECK_UNARY(s.is_super(s.right_of(s.sub(0, 3)))); + CHECK_UNARY(s.is_super(s.right_of(s.sub(0, 4)))); + CHECK_UNARY(s.is_super(s.right_of(s.sub(0, 5)))); + CHECK_UNARY(s.is_super(s.right_of(s.sub(0, 6)))); +} + +TEST_CASE("substr.compare_different_length") +{ + const char s1[] = "one empty doc"; + const char s2[] = "one empty doc, explicit termination"; + csubstr c1(s1), c2(s2); + CHECK_NE(c1, c2); + CHECK_NE(c1, s2); + CHECK_NE(s1, c2); + CHECK_LT(c1, c2); + CHECK_LT(c1, s2); + CHECK_LT(s1, c2); + CHECK_GT(c2, c1); + CHECK_GT(c2, s1); + CHECK_GT(s2, c1); + CHECK_NE((c1 > c2), (c1 < c2)); + CHECK_NE((c1 > s2), (c1 < s2)); + CHECK_NE((s1 > c2), (s1 < c2)); + CHECK_NE((c2 > c1), (c2 < c1)); + CHECK_NE((c2 > s1), (c2 < s1)); + CHECK_NE((s2 > c1), (s2 < c1)); + CHECK_NE((c1 == c2), (c1 != c2)); + CHECK_NE((c1 == s2), (c1 != s2)); + CHECK_NE((s1 == c2), (s1 != c2)); + CHECK_NE((c2 == c1), (c2 != c1)); + CHECK_NE((c2 == s1), (c2 != s1)); + CHECK_NE((s2 == c1), (s2 != c1)); +} + +TEST_CASE("substr.compare_null") +{ + csubstr s1, s2, sp(" "); + CHECK_EQ(s1, ""); + CHECK_EQ(s1, s2); + CHECK(!(s1 > s2)); + CHECK(!(s1 < s2)); + CHECK((s1 <= s2)); + CHECK((s1 >= s2)); + CHECK(!(s1 != s2)); + CHECK_EQ(s1.compare('-'), -1); + CHECK_EQ(sp.compare(' '), 0); + CHECK_EQ(s1.compare("-", 1u), -1); + CHECK_EQ(s1.compare("-", 0u), 0); + CHECK_EQ(s1.compare((const char*)0, 0u), 0); + CHECK_EQ(sp.compare((const char*)0, 0u), 1); + CHECK_EQ(sp.compare(" ", 0u), 1); + CHECK_EQ(sp.compare(" ", 1u), 0); +} + +TEST_CASE("substr.compare_vs_char") +{ + CHECK_EQ(csubstr().compare('1'), -1); // str==null, len==0 + CHECK_EQ(csubstr("0123").first(0).compare('1'), -1); // str!=null, len==0 + CHECK_EQ(csubstr("0123").first(1).compare('1'), -1); + + CHECK_EQ(csubstr("-"), '-'); + CHECK_NE(csubstr("+"), '-'); + + CHECK_NE(csubstr("---"), '-'); + CHECK_NE(csubstr("---"), "-"); + + CHECK_NE(csubstr("aaa"), 'a'); + CHECK_NE(csubstr("aaa"), "a"); + + CHECK_NE(csubstr("aaa"), 'b'); + CHECK_NE(csubstr("aaa"), "b"); + + CHECK_LT(csubstr("aaa"), 'b'); + CHECK_LT(csubstr("aaa"), "b"); + + CHECK_LE(csubstr("aaa"), 'b'); + CHECK_LE(csubstr("aaa"), "b"); + + CHECK_NE(csubstr("bbb"), 'a'); + CHECK_NE(csubstr("bbb"), "a"); + + CHECK_GT(csubstr("bbb"), 'a'); + CHECK_GT(csubstr("bbb"), "a"); + + CHECK_GE(csubstr("bbb"), 'a'); + CHECK_GE(csubstr("bbb"), "a"); +} + +TEST_CASE("substr.mixed_cmp") +{ + // c++20 introduced new comparison rules and clang10 fails: + // + // error: ISO C++20 considers use of overloaded operator '==' (with operand + // types 'const c4::basic_substring<char>' and 'const + // c4::basic_substring<char>') to be ambiguous despite there being a unique + // best viable function [-Werror,-Wambiguous-reversed-operator] + + char sa_[] = "a"; + char sb_[] = "b"; + csubstr csa = "a"; substr sa = sa_; + csubstr csb = "b"; substr sb = sb_; + + CHECK_EQ(csa, csa); + CHECK_EQ(sa, sa); // this fails + CHECK_EQ(csa, sa); + CHECK_EQ(sa, csa); + + CHECK_NE(sa, sb); + CHECK_NE(csa, csb); + CHECK_NE(csa, sb); + CHECK_NE(sa, csb); + + CHECK_LT(sa, sb); + CHECK_LT(csa, csb); + CHECK_LT(csa, sb); + CHECK_LT(sa, csb); + + CHECK_LE(sa, sb); + CHECK_LE(csa, csb); + CHECK_LE(csa, sb); + CHECK_LE(sa, csb); + + CHECK_LE(sa, sa); + CHECK_LE(csa, csa); + CHECK_LE(csa, sa); + CHECK_LE(sa, csa); + + CHECK_GT(sb, sa); + CHECK_GT(csb, csa); + CHECK_GT(csb, sa); + CHECK_GT( sb, csa); + + CHECK_GE(sb, sa); + CHECK_GE(csb, csa); + CHECK_GE(csb, sa); + CHECK_GE( sb, csa); + + CHECK_GE(sb, sb); + CHECK_GE(csb, csb); + CHECK_GE(csb, sb); + CHECK_GE( sb, csb); +} + +TEST_CASE("substr.eqne") +{ + char buf[128]; + for(size_t i = 0; i < 5; ++i) buf[i] = (char)('0' + i); + csubstr cmp(buf, 5); + + CHECK_EQ(csubstr("01234"), cmp); + CHECK_EQ( "01234" , cmp); + CHECK_EQ( cmp, "01234"); + CHECK_NE(csubstr("0123"), cmp); + CHECK_NE( "0123" , cmp); + CHECK_NE( cmp, "0123"); + CHECK_NE(csubstr("012345"), cmp); + CHECK_NE( "012345" , cmp); + CHECK_NE( cmp, "012345"); +} + +TEST_CASE("substr.substr2csubstr") +{ + char b[] = "some string"; + substr s(b); + csubstr sc = s; + CHECK_EQ(sc, s); + const substr cs(b); + const csubstr csc(b); +} + +template <class ...Args> +void test_first_of_any(csubstr input, bool true_or_false, size_t which, size_t pos, Args... args) +{ + csubstr::first_of_any_result r = input.first_of_any(to_csubstr(args)...); + //std::cout << input << ": " << (bool(r) ? "true" : "false") << "/which:" << r.which << "/pos:" << r.pos << "\n"; + CHECK_EQ(r, true_or_false); + if(true_or_false) + { + CHECK_UNARY(r); + } + else + { + CHECK_FALSE(r); + } + CHECK_EQ(r.which, which); + CHECK_EQ(r.pos, pos); +} + +TEST_CASE("substr.first_of_any") +{ + size_t NONE = csubstr::NONE; + size_t npos = csubstr::npos; + + test_first_of_any("foobar" , true , 0u , 3u, "bar", "barbell", "bark", "barff"); + test_first_of_any("foobar" , false, NONE, npos, "barbell", "bark", "barff"); + test_first_of_any("foobart" , false, NONE, npos, "barbell", "bark", "barff"); + + test_first_of_any("10" , false, NONE, npos, "0x", "0X", "-0x", "-0X"); + test_first_of_any("10]" , false, NONE, npos, "0x", "0X", "-0x", "-0X"); + test_first_of_any(csubstr("10]").first(2), false, NONE, npos, "0x", "0X", "-0x", "-0X"); + + + test_first_of_any("baz{% endif %}", true, 0u, 3u, "{% endif %}", "{% if " , "{% elif bar %}" , "{% else %}" ); + test_first_of_any("baz{% endif %}", true, 1u, 3u, "{% if " , "{% endif %}" , "{% elif bar %}" , "{% else %}" ); + test_first_of_any("baz{% endif %}", true, 2u, 3u, "{% if " , "{% elif bar %}" , "{% endif %}" , "{% else %}" ); + test_first_of_any("baz{% endif %}", true, 3u, 3u, "{% if " , "{% elif bar %}" , "{% else %}" , "{% endif %}"); + + test_first_of_any("baz{% e..if %}", false, NONE, npos, "{% endif %}", "{% if " , "{% elif bar %}" , "{% else %}" ); + test_first_of_any("baz{% e..if %}", false, NONE, npos, "{% if " , "{% endif %}" , "{% elif bar %}" , "{% else %}" ); + test_first_of_any("baz{% e..if %}", false, NONE, npos, "{% if " , "{% elif bar %}" , "{% endif %}" , "{% else %}" ); + test_first_of_any("baz{% e..if %}", false, NONE, npos, "{% if " , "{% elif bar %}" , "{% else %}" , "{% endif %}"); + + + test_first_of_any("bar{% else %}baz{% endif %}", true, 0u, 3u, "{% else %}" , "{% if " , "{% elif bar %}" , "{% endif %}"); + test_first_of_any("bar{% else %}baz{% endif %}", true, 1u, 3u, "{% if " , "{% else %}" , "{% elif bar %}" , "{% endif %}"); + test_first_of_any("bar{% else %}baz{% endif %}", true, 2u, 3u, "{% if " , "{% elif bar %}" , "{% else %}" , "{% endif %}"); + test_first_of_any("bar{% else %}baz{% endif %}", true, 3u, 3u, "{% if " , "{% elif bar %}" , "{% endif %}" , "{% else %}" ); + + test_first_of_any("bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% else %}" , "{% if " , "{% elif bar %}" , "{% endif %}"); + test_first_of_any("bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% if " , "{% else %}" , "{% elif bar %}" , "{% endif %}"); + test_first_of_any("bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% if " , "{% elif bar %}" , "{% else %}" , "{% endif %}"); + test_first_of_any("bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% if " , "{% elif bar %}" , "{% endif %}" , "{% else %}" ); + + + test_first_of_any("foo{% elif bar %}bar{% else %}baz{% endif %}", true, 0u, 3u, "{% elif bar %}" , "{% if " , "{% else %}" , "{% endif %}" ); + test_first_of_any("foo{% elif bar %}bar{% else %}baz{% endif %}", true, 1u, 3u, "{% if " , "{% elif bar %}" , "{% else %}" , "{% endif %}" ); + test_first_of_any("foo{% elif bar %}bar{% else %}baz{% endif %}", true, 2u, 3u, "{% if " , "{% else %}" , "{% elif bar %}" , "{% endif %}" ); + test_first_of_any("foo{% elif bar %}bar{% else %}baz{% endif %}", true, 3u, 3u, "{% if " , "{% else %}" , "{% endif %}" , "{% elif bar %}"); + + test_first_of_any("foo{% e..f bar %}bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% elif bar %}" , "{% if " , "{% else %}" , "{% endif %}" ); + test_first_of_any("foo{% e..f bar %}bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% if " , "{% elif bar %}" , "{% else %}" , "{% endif %}" ); + test_first_of_any("foo{% e..f bar %}bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% if " , "{% else %}" , "{% elif bar %}" , "{% endif %}" ); + test_first_of_any("foo{% e..f bar %}bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% if " , "{% else %}" , "{% endif %}" , "{% elif bar %}"); + + + test_first_of_any("{% if foo %}foo{% elif bar %}bar{% else %}baz{% endif %}", true, 0u, 0u, "{% if " , "{% elif bar %}" , "{% else %}" , "{% endif %}" ); + test_first_of_any("{% if foo %}foo{% elif bar %}bar{% else %}baz{% endif %}", true, 1u, 0u, "{% elif bar %}" , "{% if " , "{% else %}" , "{% endif %}" ); + test_first_of_any("{% if foo %}foo{% elif bar %}bar{% else %}baz{% endif %}", true, 2u, 0u, "{% elif bar %}" , "{% else %}" , "{% if " , "{% endif %}" ); + test_first_of_any("{% if foo %}foo{% elif bar %}bar{% else %}baz{% endif %}", true, 3u, 0u, "{% elif bar %}" , "{% else %}" , "{% endif %}", "{% if " ); + + test_first_of_any("{% .. foo %}foo{% e..f bar %}bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% if " , "{% elif bar %}" , "{% else %}" , "{% endif %}" ); + test_first_of_any("{% .. foo %}foo{% e..f bar %}bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% elif bar %}" , "{% if " , "{% else %}" , "{% endif %}" ); + test_first_of_any("{% .. foo %}foo{% e..f bar %}bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% elif bar %}" , "{% else %}" , "{% if " , "{% endif %}" ); + test_first_of_any("{% .. foo %}foo{% e..f bar %}bar{% e..e %}baz{% e..if %}", false, NONE, npos, "{% elif bar %}" , "{% else %}" , "{% endif %}", "{% if " ); +} + + +TEST_CASE("substr.pair_range_esc") +{ + const char q = '\''; + CHECK_EQ(csubstr("").pair_range_esc(q), ""); + CHECK_EQ(csubstr("'").pair_range_esc(q), ""); + CHECK_EQ(csubstr("''").pair_range_esc(q), "''"); + CHECK_EQ(csubstr("'\\'\\''").pair_range_esc(q), "'\\'\\''"); + CHECK_EQ(csubstr("asdasdasd''asdasd").pair_range_esc(q), "''"); + CHECK_EQ(csubstr("asdasdasd'abc'asdasda").pair_range_esc(q), "'abc'"); +} + +TEST_CASE("substr.pair_range") +{ + CHECK_EQ(csubstr("").pair_range('{', '}'), ""); + CHECK_EQ(csubstr("{").pair_range('{', '}'), ""); + CHECK_EQ(csubstr("}").pair_range('{', '}'), ""); + CHECK_EQ(csubstr("{}").pair_range('{', '}'), "{}"); + CHECK_EQ(csubstr("{abc}").pair_range('{', '}'), "{abc}"); + CHECK_EQ(csubstr("123{abc}456").pair_range('{', '}'), "{abc}"); +} + +TEST_CASE("substr.pair_range_nested") +{ + CHECK_EQ(csubstr("").pair_range_nested('{', '}'), ""); + CHECK_EQ(csubstr("{").pair_range_nested('{', '}'), ""); + CHECK_EQ(csubstr("}").pair_range_nested('{', '}'), ""); + CHECK_EQ(csubstr("{}").pair_range_nested('{', '}'), "{}"); + CHECK_EQ(csubstr("{abc}").pair_range_nested('{', '}'), "{abc}"); + CHECK_EQ(csubstr("123{abc}456").pair_range_nested('{', '}'), "{abc}"); + CHECK_EQ(csubstr("123{abc}456{def}").pair_range_nested('{', '}'), "{abc}"); + CHECK_EQ(csubstr( "{{}}").pair_range_nested('{', '}'), "{{}}"); + CHECK_EQ(csubstr("123{{}}456").pair_range_nested('{', '}'), "{{}}"); + CHECK_EQ(csubstr( "{a{}b{}c}").pair_range_nested('{', '}'), "{a{}b{}c}"); + CHECK_EQ(csubstr("123{a{}b{}c}456").pair_range_nested('{', '}'), "{a{}b{}c}"); + CHECK_EQ(csubstr( "{a{{}}b{{}}c}").pair_range_nested('{', '}'), "{a{{}}b{{}}c}"); + CHECK_EQ(csubstr("123{a{{}}b{{}}c}456").pair_range_nested('{', '}'), "{a{{}}b{{}}c}"); + CHECK_EQ(csubstr( "{{{}}a{{}}b{{}}c{{}}}").pair_range_nested('{', '}'), "{{{}}a{{}}b{{}}c{{}}}"); + CHECK_EQ(csubstr("123{{{}}a{{}}b{{}}c{{}}}456").pair_range_nested('{', '}'), "{{{}}a{{}}b{{}}c{{}}}"); +} + +TEST_CASE("substr.unquoted") +{ + CHECK_EQ(csubstr("").unquoted(), ""); + + CHECK_EQ(csubstr("''").unquoted(), ""); + CHECK_EQ(csubstr("\"\"").unquoted(), ""); + + CHECK_EQ(csubstr("'\''").unquoted(), "'"); + + CHECK_EQ(csubstr("aa").unquoted(), "aa"); + CHECK_EQ(csubstr("'aa'").unquoted(), "aa"); + CHECK_EQ(csubstr("\"aa\"").unquoted(), "aa"); + CHECK_EQ(csubstr("'aa\''").unquoted(), "aa'"); +} + + +TEST_CASE("substr.first_non_empty_span") +{ + CHECK_EQ(csubstr("foo bar").first_non_empty_span(), "foo"); + CHECK_EQ(csubstr(" foo bar").first_non_empty_span(), "foo"); + CHECK_EQ(csubstr("\n \r \t foo bar").first_non_empty_span(), "foo"); + CHECK_EQ(csubstr("\n \r \t foo\n\r\t bar").first_non_empty_span(), "foo"); + CHECK_EQ(csubstr("\n \r \t foo\n\r\t bar").first_non_empty_span(), "foo"); + CHECK_EQ(csubstr(",\n \r \t foo\n\r\t bar").first_non_empty_span(), ","); +} + +TEST_CASE("substr.first_uint_span") +{ + CHECK_EQ(csubstr("1234").first_uint_span(), "1234"); + CHECK_EQ(csubstr("+1234").first_uint_span(), "+1234"); + CHECK_EQ(csubstr("-1234").first_uint_span(), ""); + CHECK_EQ(csubstr("1234 asdkjh").first_uint_span(), "1234"); + CHECK_EQ(csubstr("1234\rasdkjh").first_uint_span(), "1234"); + CHECK_EQ(csubstr("1234\tasdkjh").first_uint_span(), "1234"); + CHECK_EQ(csubstr("1234\nasdkjh").first_uint_span(), "1234"); + CHECK_EQ(csubstr("1234]asdkjh").first_uint_span(), "1234"); + CHECK_EQ(csubstr("1234)asdkjh").first_uint_span(), "1234"); + CHECK_EQ(csubstr("1234gasdkjh").first_uint_span(), ""); + CHECK_EQ(csubstr("1").first_uint_span(), "1"); + CHECK_EQ(csubstr("+1").first_uint_span(), "+1"); + CHECK_EQ(csubstr("-1").first_uint_span(), ""); + CHECK_EQ(csubstr("-0").first_uint_span(), ""); + CHECK_EQ(csubstr("0").first_uint_span(), "0"); + CHECK_EQ(csubstr("+0").first_uint_span(), "+0"); + CHECK_EQ(csubstr("-0").first_uint_span(), ""); + CHECK_EQ(csubstr("1234 abc").first_uint_span(), "1234"); + CHECK_EQ(csubstr("abc 1234 abc").first_uint_span(), ""); + CHECK_EQ(csubstr("+0x1234 abc").first_uint_span(), "+0x1234"); + CHECK_EQ(csubstr("-0x1234 abc").first_uint_span(), ""); + CHECK_EQ(csubstr("0x1234 abc").first_uint_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234\rabc").first_uint_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234\nabc").first_uint_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234\tabc").first_uint_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234]abc").first_uint_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234)abc").first_uint_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234g").first_uint_span(), ""); + CHECK_EQ(csubstr("0b01").first_uint_span(), "0b01"); + CHECK_EQ(csubstr("+0b01").first_uint_span(), "+0b01"); + CHECK_EQ(csubstr("-0b01").first_uint_span(), ""); + CHECK_EQ(csubstr("0b01 asdasd").first_uint_span(), "0b01"); + CHECK_EQ(csubstr("0b01\rasdasd").first_uint_span(), "0b01"); + CHECK_EQ(csubstr("0b01\tasdasd").first_uint_span(), "0b01"); + CHECK_EQ(csubstr("0b01\nasdasd").first_uint_span(), "0b01"); + CHECK_EQ(csubstr("0b01]asdasd").first_uint_span(), "0b01"); + CHECK_EQ(csubstr("0b01)asdasd").first_uint_span(), "0b01"); + CHECK_EQ(csubstr("0b01hasdasd").first_uint_span(), ""); + CHECK_EQ(csubstr("+").first_uint_span(), ""); + CHECK_EQ(csubstr("-").first_uint_span(), ""); +} + +TEST_CASE("substr.first_int_span") +{ + CHECK_EQ(csubstr("1234").first_int_span(), "1234"); + CHECK_EQ(csubstr("+1234").first_int_span(), "+1234"); + CHECK_EQ(csubstr("-1234").first_int_span(), "-1234"); + CHECK_EQ(csubstr("-1234 asdkjh").first_int_span(), "-1234"); + CHECK_EQ(csubstr("-1234\rasdkjh").first_int_span(), "-1234"); + CHECK_EQ(csubstr("-1234\tasdkjh").first_int_span(), "-1234"); + CHECK_EQ(csubstr("-1234\nasdkjh").first_int_span(), "-1234"); + CHECK_EQ(csubstr("-1234]asdkjh").first_int_span(), "-1234"); + CHECK_EQ(csubstr("-1234)asdkjh").first_int_span(), "-1234"); + CHECK_EQ(csubstr("-1234gasdkjh").first_int_span(), ""); + CHECK_EQ(csubstr("1").first_int_span(), "1"); + CHECK_EQ(csubstr("+1").first_int_span(), "+1"); + CHECK_EQ(csubstr("-1").first_int_span(), "-1"); + CHECK_EQ(csubstr("-0").first_int_span(), "-0"); + CHECK_EQ(csubstr("0").first_int_span(), "0"); + CHECK_EQ(csubstr("+0").first_int_span(), "+0"); + CHECK_EQ(csubstr("-0").first_int_span(), "-0"); + CHECK_EQ(csubstr("1234 abc").first_int_span(), "1234"); + CHECK_EQ(csubstr("abc 1234 abc").first_int_span(), ""); + CHECK_EQ(csubstr("+0x1234 abc").first_int_span(), "+0x1234"); + CHECK_EQ(csubstr("-0x1234 abc").first_int_span(), "-0x1234"); + CHECK_EQ(csubstr("0x1234 abc").first_int_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234\rabc").first_int_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234\nabc").first_int_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234\tabc").first_int_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234]abc").first_int_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234)abc").first_int_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234gabc").first_int_span(), ""); + CHECK_EQ(csubstr("0b01").first_int_span(), "0b01"); + CHECK_EQ(csubstr("+0b01").first_int_span(), "+0b01"); + CHECK_EQ(csubstr("-0b01").first_int_span(), "-0b01"); + CHECK_EQ(csubstr("0b01 asdasd").first_int_span(), "0b01"); + CHECK_EQ(csubstr("0b01\rasdasd").first_int_span(), "0b01"); + CHECK_EQ(csubstr("0b01\tasdasd").first_int_span(), "0b01"); + CHECK_EQ(csubstr("0b01\nasdasd").first_int_span(), "0b01"); + CHECK_EQ(csubstr("0b01]asdasd").first_int_span(), "0b01"); + CHECK_EQ(csubstr("0b01)asdasd").first_int_span(), "0b01"); + CHECK_EQ(csubstr("0b01gasdasd").first_int_span(), ""); +} + +TEST_CASE("substr.first_real_span") +{ + // all integers are reals + CHECK_EQ(csubstr("1234").first_real_span(), "1234"); + CHECK_EQ(csubstr("+1234").first_real_span(), "+1234"); + CHECK_EQ(csubstr("-1234").first_real_span(), "-1234"); + CHECK_EQ(csubstr("-1234 asdkjh").first_real_span(), "-1234"); + CHECK_EQ(csubstr("-1234\rasdkjh").first_real_span(), "-1234"); + CHECK_EQ(csubstr("-1234\tasdkjh").first_real_span(), "-1234"); + CHECK_EQ(csubstr("-1234\nasdkjh").first_real_span(), "-1234"); + CHECK_EQ(csubstr("-1234]asdkjh").first_real_span(), "-1234"); + CHECK_EQ(csubstr("-1234)asdkjh").first_real_span(), "-1234"); + CHECK_EQ(csubstr("-1234gasdkjh").first_real_span(), ""); + CHECK_EQ(csubstr("1").first_real_span(), "1"); + CHECK_EQ(csubstr("+1").first_real_span(), "+1"); + CHECK_EQ(csubstr("-1").first_real_span(), "-1"); + CHECK_EQ(csubstr("-0").first_real_span(), "-0"); + CHECK_EQ(csubstr("0").first_real_span(), "0"); + CHECK_EQ(csubstr("+0").first_real_span(), "+0"); + CHECK_EQ(csubstr("-0").first_real_span(), "-0"); + CHECK_EQ(csubstr("1234 abc").first_real_span(), "1234"); + CHECK_EQ(csubstr("abc 1234 abc").first_real_span(), ""); + CHECK_EQ(csubstr("+0x1234 abc").first_real_span(), "+0x1234"); + CHECK_EQ(csubstr("-0x1234 abc").first_real_span(), "-0x1234"); + CHECK_EQ(csubstr("0x1234 abc").first_real_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234\rabc").first_real_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234\nabc").first_real_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234\tabc").first_real_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234]abc").first_real_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234)abc").first_real_span(), "0x1234"); + CHECK_EQ(csubstr("0x1234gabc").first_real_span(), ""); + CHECK_EQ(csubstr("0b01").first_real_span(), "0b01"); + CHECK_EQ(csubstr("+0b01").first_real_span(), "+0b01"); + CHECK_EQ(csubstr("-0b01").first_real_span(), "-0b01"); + CHECK_EQ(csubstr("0b01 asdasd").first_real_span(), "0b01"); + CHECK_EQ(csubstr("0b01\rasdasd").first_real_span(), "0b01"); + CHECK_EQ(csubstr("0b01\tasdasd").first_real_span(), "0b01"); + CHECK_EQ(csubstr("0b01\nasdasd").first_real_span(), "0b01"); + CHECK_EQ(csubstr("0b01]asdasd").first_real_span(), "0b01"); + CHECK_EQ(csubstr("0b01)asdasd").first_real_span(), "0b01"); + CHECK_EQ(csubstr("0b01gasdasd").first_real_span(), ""); + CHECK_EQ(csubstr("0b1.01 asdasd").first_real_span(), "0b1.01"); + CHECK_EQ(csubstr("0b1.01\rasdasd").first_real_span(), "0b1.01"); + CHECK_EQ(csubstr("0b1.01\tasdasd").first_real_span(), "0b1.01"); + CHECK_EQ(csubstr("0b1.01\nasdasd").first_real_span(), "0b1.01"); + CHECK_EQ(csubstr("0b1.01]asdasd").first_real_span(), "0b1.01"); + CHECK_EQ(csubstr("0b1.01)asdasd").first_real_span(), "0b1.01"); + CHECK_EQ(csubstr("0b1.01gasdasd").first_real_span(), ""); + CHECK_EQ(csubstr("0b1.02 asdasd").first_real_span(), ""); + CHECK_EQ(csubstr("0b1.02\rasdasd").first_real_span(), ""); + CHECK_EQ(csubstr("0b1.02\tasdasd").first_real_span(), ""); + CHECK_EQ(csubstr("0b1.02\nasdasd").first_real_span(), ""); + CHECK_EQ(csubstr("0b1.02]asdasd").first_real_span(), ""); + CHECK_EQ(csubstr("0b1.02)asdasd").first_real_span(), ""); + CHECK_EQ(csubstr("0b1.02gasdasd").first_real_span(), ""); + CHECK_EQ(csubstr("+").first_real_span(), ""); + CHECK_EQ(csubstr("-").first_real_span(), ""); + CHECK_EQ(csubstr("+0x").first_real_span(), ""); + CHECK_EQ(csubstr("-0x").first_real_span(), ""); + CHECK_EQ(csubstr("+0b").first_real_span(), ""); + CHECK_EQ(csubstr("-0b").first_real_span(), ""); + CHECK_EQ(csubstr("+0o").first_real_span(), ""); + CHECK_EQ(csubstr("-0o").first_real_span(), ""); + CHECK_EQ(csubstr("-1.234 asdkjh").first_real_span(), "-1.234"); +// CHECK_EQ(csubstr("-1.234e5 asdkjh").first_real_span(), "-1.234e5"); + CHECK_EQ(csubstr("-1.234e+5 asdkjh").first_real_span(), "-1.234e+5"); + CHECK_EQ(csubstr("-1.234e-5 asdkjh").first_real_span(), "-1.234e-5"); + CHECK_EQ(csubstr("0x1.e8480p+19 asdkjh").first_real_span(), "0x1.e8480p+19"); + CHECK_EQ(csubstr("0x1.e8480p-19 asdkjh").first_real_span(), "0x1.e8480p-19"); + CHECK_EQ(csubstr("-0x1.e8480p+19 asdkjh").first_real_span(), "-0x1.e8480p+19"); + CHECK_EQ(csubstr("-0x1.e8480p-19 asdkjh").first_real_span(), "-0x1.e8480p-19"); + CHECK_EQ(csubstr("+0x1.e8480p+19 asdkjh").first_real_span(), "+0x1.e8480p+19"); + CHECK_EQ(csubstr("+0x1.e8480p-19 asdkjh").first_real_span(), "+0x1.e8480p-19"); + CHECK_EQ(csubstr("infinity").first_real_span(), "infinity"); + CHECK_EQ(csubstr(" infinity").first_real_span(), "infinity"); + CHECK_EQ(csubstr("-infinity").first_real_span(), "-infinity"); + CHECK_EQ(csubstr(" -infinity").first_real_span(), "-infinity"); + CHECK_EQ(csubstr("+infinity").first_real_span(), "+infinity"); + CHECK_EQ(csubstr(" +infinity").first_real_span(), "+infinity"); + CHECK_EQ(csubstr("infinity ").first_real_span(), "infinity"); + CHECK_EQ(csubstr(" infinity ").first_real_span(), "infinity"); + CHECK_EQ(csubstr("-infinity ").first_real_span(), "-infinity"); + CHECK_EQ(csubstr(" -infinity ").first_real_span(), "-infinity"); + CHECK_EQ(csubstr("+infinity ").first_real_span(), "+infinity"); + CHECK_EQ(csubstr(" +infinity ").first_real_span(), "+infinity"); + CHECK_EQ(csubstr("infinity1").first_real_span(), ""); + CHECK_EQ(csubstr(" infinity1").first_real_span(), ""); + CHECK_EQ(csubstr("-infinity1").first_real_span(), ""); + CHECK_EQ(csubstr(" -infinity1").first_real_span(), ""); + CHECK_EQ(csubstr("+infinity1").first_real_span(), ""); + CHECK_EQ(csubstr(" +infinity1").first_real_span(), ""); + CHECK_EQ(csubstr("infin").first_real_span(), ""); + CHECK_EQ(csubstr(" infin").first_real_span(), ""); + CHECK_EQ(csubstr("-infin").first_real_span(), ""); + CHECK_EQ(csubstr(" -infin").first_real_span(), ""); + CHECK_EQ(csubstr("+infin").first_real_span(), ""); + CHECK_EQ(csubstr(" +infin").first_real_span(), ""); + CHECK_EQ(csubstr("inflated").first_real_span(), ""); + CHECK_EQ(csubstr(" inflated").first_real_span(), ""); + CHECK_EQ(csubstr("-inflated").first_real_span(), ""); + CHECK_EQ(csubstr(" -inflated").first_real_span(), ""); + CHECK_EQ(csubstr("+inflated").first_real_span(), ""); + CHECK_EQ(csubstr(" +inflated").first_real_span(), ""); + CHECK_EQ(csubstr("inf").first_real_span(), "inf"); + CHECK_EQ(csubstr(" inf").first_real_span(), "inf"); + CHECK_EQ(csubstr("-inf").first_real_span(), "-inf"); + CHECK_EQ(csubstr(" -inf").first_real_span(), "-inf"); + CHECK_EQ(csubstr("+inf").first_real_span(), "+inf"); + CHECK_EQ(csubstr(" +inf").first_real_span(), "+inf"); + CHECK_EQ(csubstr("inf ").first_real_span(), "inf"); + CHECK_EQ(csubstr(" inf ").first_real_span(), "inf"); + CHECK_EQ(csubstr("-inf ").first_real_span(), "-inf"); + CHECK_EQ(csubstr(" -inf ").first_real_span(), "-inf"); + CHECK_EQ(csubstr("+inf ").first_real_span(), "+inf"); + CHECK_EQ(csubstr(" +inf ").first_real_span(), "+inf"); + CHECK_EQ(csubstr("inf1").first_real_span(), ""); + CHECK_EQ(csubstr(" inf1").first_real_span(), ""); + CHECK_EQ(csubstr("-inf1").first_real_span(), ""); + CHECK_EQ(csubstr(" -inf1").first_real_span(), ""); + CHECK_EQ(csubstr("+inf1").first_real_span(), ""); + CHECK_EQ(csubstr(" +inf1").first_real_span(), ""); + CHECK_EQ(csubstr("nan").first_real_span(), "nan"); + CHECK_EQ(csubstr(" nan").first_real_span(), "nan"); + CHECK_EQ(csubstr("-nan").first_real_span(), "-nan"); + CHECK_EQ(csubstr(" -nan").first_real_span(), "-nan"); + CHECK_EQ(csubstr("+nan").first_real_span(), "+nan"); + CHECK_EQ(csubstr(" +nan").first_real_span(), "+nan"); + CHECK_EQ(csubstr("nan ").first_real_span(), "nan"); + CHECK_EQ(csubstr(" nan ").first_real_span(), "nan"); + CHECK_EQ(csubstr("-nan ").first_real_span(), "-nan"); + CHECK_EQ(csubstr(" -nan ").first_real_span(), "-nan"); + CHECK_EQ(csubstr("+nan ").first_real_span(), "+nan"); + CHECK_EQ(csubstr(" +nan ").first_real_span(), "+nan"); + CHECK_EQ(csubstr("nan1").first_real_span(), ""); + CHECK_EQ(csubstr(" nan1").first_real_span(), ""); + CHECK_EQ(csubstr("-nan1").first_real_span(), ""); + CHECK_EQ(csubstr(" -nan1").first_real_span(), ""); + CHECK_EQ(csubstr("+nan1").first_real_span(), ""); + CHECK_EQ(csubstr(" +nan1").first_real_span(), ""); +} + +// start with some obvious direct tests +TEST_CASE("substr.is_unsigned_integer") +{ + SUBCASE("empty_string") + { + CHECK_FALSE(csubstr().is_unsigned_integer()); + CHECK_FALSE(csubstr("").is_unsigned_integer()); + } + SUBCASE("signs") + { + CHECK_FALSE(csubstr("-").is_unsigned_integer()); + CHECK_FALSE(csubstr("+").is_unsigned_integer()); + CHECK_FALSE(csubstr("-1").is_unsigned_integer()); + CHECK_UNARY(csubstr("+1").is_unsigned_integer()); + } + SUBCASE("whitespace_before") + { + CHECK_FALSE(csubstr(" 0").is_unsigned_integer()); + CHECK_FALSE(csubstr(" 1").is_unsigned_integer()); + CHECK_FALSE(csubstr(" -1").is_unsigned_integer()); + CHECK_FALSE(csubstr(" 0.1").is_unsigned_integer()); + CHECK_FALSE(csubstr(" -0.1").is_unsigned_integer()); + } + SUBCASE("whitespace_after") + { + CHECK_FALSE(csubstr("0 ").is_unsigned_integer()); + CHECK_FALSE(csubstr("1 ").is_unsigned_integer()); + CHECK_FALSE(csubstr("-1 ").is_unsigned_integer()); + CHECK_FALSE(csubstr("0.1 ").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0.1 ").is_unsigned_integer()); + } + SUBCASE("whitespace_only") + { + CHECK_FALSE(csubstr(" ").is_unsigned_integer()); + CHECK_FALSE(csubstr("\t\t\t\t").is_unsigned_integer()); + CHECK_FALSE(csubstr("\n\n\n\n").is_unsigned_integer()); + CHECK_FALSE(csubstr("\r\r\r\r").is_unsigned_integer()); + } + SUBCASE("decimal") + { + CHECK_UNARY(csubstr("0").is_unsigned_integer()); + CHECK_UNARY(csubstr("1").is_unsigned_integer()); + CHECK_FALSE(csubstr("-1").is_unsigned_integer()); + CHECK_FALSE(csubstr("0.1").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0.1").is_unsigned_integer()); + } + SUBCASE("hexadecimal") + { + CHECK_FALSE(csubstr("0x").is_unsigned_integer()); + CHECK_FALSE(csubstr("0X").is_unsigned_integer()); + CHECK_UNARY(csubstr("0x1").is_unsigned_integer()); + CHECK_UNARY(csubstr("0X1").is_unsigned_integer()); + CHECK_UNARY(csubstr("0x0").is_unsigned_integer()); + CHECK_UNARY(csubstr("0X0").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xa").is_unsigned_integer()); + CHECK_UNARY(csubstr("0Xa").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xb").is_unsigned_integer()); + CHECK_UNARY(csubstr("0Xb").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xc").is_unsigned_integer()); + CHECK_UNARY(csubstr("0Xc").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xd").is_unsigned_integer()); + CHECK_UNARY(csubstr("0Xd").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xe").is_unsigned_integer()); + CHECK_UNARY(csubstr("0Xe").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xf").is_unsigned_integer()); + CHECK_UNARY(csubstr("0Xf").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xA").is_unsigned_integer()); + CHECK_UNARY(csubstr("0XA").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xB").is_unsigned_integer()); + CHECK_UNARY(csubstr("0XB").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xC").is_unsigned_integer()); + CHECK_UNARY(csubstr("0XC").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xD").is_unsigned_integer()); + CHECK_UNARY(csubstr("0XD").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xE").is_unsigned_integer()); + CHECK_UNARY(csubstr("0XE").is_unsigned_integer()); + CHECK_UNARY(csubstr("0xF").is_unsigned_integer()); + CHECK_UNARY(csubstr("0XF").is_unsigned_integer()); + CHECK_FALSE(csubstr("0xg").is_unsigned_integer()); + CHECK_FALSE(csubstr("0Xg").is_unsigned_integer()); + CHECK_FALSE(csubstr("0xG").is_unsigned_integer()); + CHECK_FALSE(csubstr("0XG").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0x1").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0X1").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0x0").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0X0").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xa").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0Xa").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xb").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0Xb").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xc").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0Xc").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xd").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0Xd").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xe").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0Xe").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xf").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0Xf").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xA").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0XA").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xB").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0XB").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xC").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0XC").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xD").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0XD").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xE").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0XE").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xF").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0XF").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xg").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0Xg").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0xG").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0XG").is_unsigned_integer()); + } + SUBCASE("binary") + { + CHECK_FALSE(csubstr("0b").is_unsigned_integer()); + CHECK_FALSE(csubstr("0B").is_unsigned_integer()); + CHECK_UNARY(csubstr("0b0").is_unsigned_integer()); + CHECK_UNARY(csubstr("0B0").is_unsigned_integer()); + CHECK_UNARY(csubstr("0b1").is_unsigned_integer()); + CHECK_UNARY(csubstr("0B1").is_unsigned_integer()); + CHECK_FALSE(csubstr("0b2").is_unsigned_integer()); + CHECK_FALSE(csubstr("0B2").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0b0").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0B0").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0b1").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0B1").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0b2").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0B2").is_unsigned_integer()); + } + SUBCASE("octal") + { + CHECK_FALSE(csubstr("0o").is_unsigned_integer()); + CHECK_FALSE(csubstr("0O").is_unsigned_integer()); + CHECK_UNARY(csubstr("0o0").is_unsigned_integer()); + CHECK_UNARY(csubstr("0O0").is_unsigned_integer()); + CHECK_UNARY(csubstr("0o1").is_unsigned_integer()); + CHECK_UNARY(csubstr("0O1").is_unsigned_integer()); + CHECK_UNARY(csubstr("0o6").is_unsigned_integer()); + CHECK_UNARY(csubstr("0O6").is_unsigned_integer()); + CHECK_UNARY(csubstr("0o6").is_unsigned_integer()); + CHECK_UNARY(csubstr("0O6").is_unsigned_integer()); + CHECK_UNARY(csubstr("0o7").is_unsigned_integer()); + CHECK_UNARY(csubstr("0O7").is_unsigned_integer()); + CHECK_FALSE(csubstr("0o8").is_unsigned_integer()); + CHECK_FALSE(csubstr("0O8").is_unsigned_integer()); + CHECK_FALSE(csubstr("0o9").is_unsigned_integer()); + CHECK_FALSE(csubstr("0O9").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0o0").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0O0").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0o1").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0O1").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0o6").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0O6").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0o6").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0O6").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0o7").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0O7").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0o8").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0O8").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0o9").is_unsigned_integer()); + CHECK_FALSE(csubstr("-0O9").is_unsigned_integer()); + } +} + +TEST_CASE("substr.is_integer") +{ + SUBCASE("empty_string") + { + CHECK_FALSE(csubstr().is_integer()); + CHECK_FALSE(csubstr("").is_integer()); + } + SUBCASE("signs") + { + CHECK_FALSE(csubstr("-").is_integer()); + CHECK_FALSE(csubstr("+").is_integer()); + CHECK_UNARY(csubstr("-1").is_integer()); + CHECK_UNARY(csubstr("+1").is_integer()); + } + SUBCASE("whitespace_before") + { + CHECK_FALSE(csubstr(" 0").is_integer()); + CHECK_FALSE(csubstr(" 1").is_integer()); + CHECK_FALSE(csubstr(" -1").is_integer()); + CHECK_FALSE(csubstr(" 0.1").is_integer()); + CHECK_FALSE(csubstr(" -0.1").is_integer()); + } + SUBCASE("whitespace_after") + { + CHECK_FALSE(csubstr("0 ").is_integer()); + CHECK_FALSE(csubstr("1 ").is_integer()); + CHECK_FALSE(csubstr("-1 ").is_integer()); + CHECK_FALSE(csubstr("0.1 ").is_integer()); + CHECK_FALSE(csubstr("-0.1 ").is_integer()); + } + SUBCASE("whitespace_only") + { + CHECK_FALSE(csubstr(" ").is_integer()); + CHECK_FALSE(csubstr("\t\t\t\t").is_integer()); + CHECK_FALSE(csubstr("\n\n\n\n").is_integer()); + CHECK_FALSE(csubstr("\r\r\r\r").is_integer()); + } + SUBCASE("decimal") + { + CHECK_UNARY(csubstr("0").is_integer()); + CHECK_UNARY(csubstr("1").is_integer()); + CHECK_UNARY(csubstr("-1").is_integer()); + CHECK_FALSE(csubstr("0.1").is_integer()); + CHECK_FALSE(csubstr("-0.1").is_integer()); + } + SUBCASE("hexadecimal") + { + CHECK_FALSE(csubstr("0x").is_integer()); + CHECK_FALSE(csubstr("0X").is_integer()); + CHECK_UNARY(csubstr("0x1").is_integer()); + CHECK_UNARY(csubstr("0X1").is_integer()); + CHECK_UNARY(csubstr("0x0").is_integer()); + CHECK_UNARY(csubstr("0X0").is_integer()); + CHECK_UNARY(csubstr("0xa").is_integer()); + CHECK_UNARY(csubstr("0Xa").is_integer()); + CHECK_UNARY(csubstr("0xb").is_integer()); + CHECK_UNARY(csubstr("0Xb").is_integer()); + CHECK_UNARY(csubstr("0xc").is_integer()); + CHECK_UNARY(csubstr("0Xc").is_integer()); + CHECK_UNARY(csubstr("0xd").is_integer()); + CHECK_UNARY(csubstr("0Xd").is_integer()); + CHECK_UNARY(csubstr("0xe").is_integer()); + CHECK_UNARY(csubstr("0Xe").is_integer()); + CHECK_UNARY(csubstr("0xf").is_integer()); + CHECK_UNARY(csubstr("0Xf").is_integer()); + CHECK_UNARY(csubstr("0xA").is_integer()); + CHECK_UNARY(csubstr("0XA").is_integer()); + CHECK_UNARY(csubstr("0xB").is_integer()); + CHECK_UNARY(csubstr("0XB").is_integer()); + CHECK_UNARY(csubstr("0xC").is_integer()); + CHECK_UNARY(csubstr("0XC").is_integer()); + CHECK_UNARY(csubstr("0xD").is_integer()); + CHECK_UNARY(csubstr("0XD").is_integer()); + CHECK_UNARY(csubstr("0xE").is_integer()); + CHECK_UNARY(csubstr("0XE").is_integer()); + CHECK_UNARY(csubstr("0xF").is_integer()); + CHECK_UNARY(csubstr("0XF").is_integer()); + CHECK_FALSE(csubstr("0xg").is_integer()); + CHECK_FALSE(csubstr("0Xg").is_integer()); + CHECK_FALSE(csubstr("0xG").is_integer()); + CHECK_FALSE(csubstr("0XG").is_integer()); + CHECK_UNARY(csubstr("-0x1").is_integer()); + CHECK_UNARY(csubstr("-0X1").is_integer()); + CHECK_UNARY(csubstr("-0x0").is_integer()); + CHECK_UNARY(csubstr("-0X0").is_integer()); + CHECK_UNARY(csubstr("-0xa").is_integer()); + CHECK_UNARY(csubstr("-0Xa").is_integer()); + CHECK_UNARY(csubstr("-0xb").is_integer()); + CHECK_UNARY(csubstr("-0Xb").is_integer()); + CHECK_UNARY(csubstr("-0xc").is_integer()); + CHECK_UNARY(csubstr("-0Xc").is_integer()); + CHECK_UNARY(csubstr("-0xd").is_integer()); + CHECK_UNARY(csubstr("-0Xd").is_integer()); + CHECK_UNARY(csubstr("-0xe").is_integer()); + CHECK_UNARY(csubstr("-0Xe").is_integer()); + CHECK_UNARY(csubstr("-0xf").is_integer()); + CHECK_UNARY(csubstr("-0Xf").is_integer()); + CHECK_UNARY(csubstr("-0xA").is_integer()); + CHECK_UNARY(csubstr("-0XA").is_integer()); + CHECK_UNARY(csubstr("-0xB").is_integer()); + CHECK_UNARY(csubstr("-0XB").is_integer()); + CHECK_UNARY(csubstr("-0xC").is_integer()); + CHECK_UNARY(csubstr("-0XC").is_integer()); + CHECK_UNARY(csubstr("-0xD").is_integer()); + CHECK_UNARY(csubstr("-0XD").is_integer()); + CHECK_UNARY(csubstr("-0xE").is_integer()); + CHECK_UNARY(csubstr("-0XE").is_integer()); + CHECK_UNARY(csubstr("-0xF").is_integer()); + CHECK_UNARY(csubstr("-0XF").is_integer()); + CHECK_FALSE(csubstr("-0xg").is_integer()); + CHECK_FALSE(csubstr("-0Xg").is_integer()); + CHECK_FALSE(csubstr("-0xG").is_integer()); + CHECK_FALSE(csubstr("-0XG").is_integer()); + } + SUBCASE("binary") + { + CHECK_FALSE(csubstr("0b").is_integer()); + CHECK_FALSE(csubstr("0B").is_integer()); + CHECK_UNARY(csubstr("0b0").is_integer()); + CHECK_UNARY(csubstr("0B0").is_integer()); + CHECK_UNARY(csubstr("0b1").is_integer()); + CHECK_UNARY(csubstr("0B1").is_integer()); + CHECK_FALSE(csubstr("0b2").is_integer()); + CHECK_FALSE(csubstr("0B2").is_integer()); + CHECK_UNARY(csubstr("-0b0").is_integer()); + CHECK_UNARY(csubstr("-0B0").is_integer()); + CHECK_UNARY(csubstr("-0b1").is_integer()); + CHECK_UNARY(csubstr("-0B1").is_integer()); + CHECK_FALSE(csubstr("-0b2").is_integer()); + CHECK_FALSE(csubstr("-0B2").is_integer()); + } + SUBCASE("octal") + { + CHECK_FALSE(csubstr("0o").is_integer()); + CHECK_FALSE(csubstr("0O").is_integer()); + CHECK_UNARY(csubstr("0o0").is_integer()); + CHECK_UNARY(csubstr("0O0").is_integer()); + CHECK_UNARY(csubstr("0o1").is_integer()); + CHECK_UNARY(csubstr("0O1").is_integer()); + CHECK_UNARY(csubstr("0o6").is_integer()); + CHECK_UNARY(csubstr("0O6").is_integer()); + CHECK_UNARY(csubstr("0o6").is_integer()); + CHECK_UNARY(csubstr("0O6").is_integer()); + CHECK_UNARY(csubstr("0o7").is_integer()); + CHECK_UNARY(csubstr("0O7").is_integer()); + CHECK_FALSE(csubstr("0o8").is_integer()); + CHECK_FALSE(csubstr("0O8").is_integer()); + CHECK_FALSE(csubstr("0o9").is_integer()); + CHECK_FALSE(csubstr("0O9").is_integer()); + CHECK_UNARY(csubstr("-0o0").is_integer()); + CHECK_UNARY(csubstr("-0O0").is_integer()); + CHECK_UNARY(csubstr("-0o1").is_integer()); + CHECK_UNARY(csubstr("-0O1").is_integer()); + CHECK_UNARY(csubstr("-0o6").is_integer()); + CHECK_UNARY(csubstr("-0O6").is_integer()); + CHECK_UNARY(csubstr("-0o6").is_integer()); + CHECK_UNARY(csubstr("-0O6").is_integer()); + CHECK_UNARY(csubstr("-0o7").is_integer()); + CHECK_UNARY(csubstr("-0O7").is_integer()); + CHECK_FALSE(csubstr("-0o8").is_integer()); + CHECK_FALSE(csubstr("-0O8").is_integer()); + CHECK_FALSE(csubstr("-0o9").is_integer()); + CHECK_FALSE(csubstr("-0O9").is_integer()); + } +} + +TEST_CASE("substr.is_real") +{ + SUBCASE("empty_string") + { + CHECK_FALSE(csubstr().is_real()); + CHECK_FALSE(csubstr("").is_real()); + } + SUBCASE("signs") + { + CHECK_FALSE(csubstr("-").is_real()); + CHECK_FALSE(csubstr("+").is_real()); + CHECK_UNARY(csubstr("-1").is_real()); + CHECK_UNARY(csubstr("+1").is_real()); + } + SUBCASE("whitespace_before") + { + CHECK_FALSE(csubstr(" 0").is_real()); + CHECK_FALSE(csubstr(" 1").is_real()); + CHECK_FALSE(csubstr(" -1").is_real()); + CHECK_FALSE(csubstr(" 0.1").is_real()); + CHECK_FALSE(csubstr(" -0.1").is_real()); + } + SUBCASE("whitespace_after") + { + CHECK_FALSE(csubstr("0 ").is_real()); + CHECK_FALSE(csubstr("1 ").is_real()); + CHECK_FALSE(csubstr("-1 ").is_real()); + CHECK_FALSE(csubstr("0.1 ").is_real()); + CHECK_FALSE(csubstr("-0.1 ").is_real()); + } + SUBCASE("whitespace_only") + { + CHECK_FALSE(csubstr(" ").is_real()); + CHECK_FALSE(csubstr("\t\t\t\t").is_real()); + CHECK_FALSE(csubstr("\n\n\n\n").is_real()); + CHECK_FALSE(csubstr("\r\r\r\r").is_real()); + } + SUBCASE("decimal") + { + CHECK_UNARY(csubstr("0").is_real()); + CHECK_UNARY(csubstr("1").is_real()); + CHECK_UNARY(csubstr("-1").is_real()); + CHECK_UNARY(csubstr("0.1").is_real()); + CHECK_UNARY(csubstr("-0.1").is_real()); + } + SUBCASE("hexadecimal") + { + CHECK_FALSE(csubstr("0x").is_real()); + CHECK_FALSE(csubstr("0X").is_real()); + CHECK_UNARY(csubstr("0x1").is_real()); + CHECK_UNARY(csubstr("0X1").is_real()); + CHECK_UNARY(csubstr("0x0").is_real()); + CHECK_UNARY(csubstr("0X0").is_real()); + CHECK_UNARY(csubstr("0xa").is_real()); + CHECK_UNARY(csubstr("0Xa").is_real()); + CHECK_UNARY(csubstr("0xb").is_real()); + CHECK_UNARY(csubstr("0Xb").is_real()); + CHECK_UNARY(csubstr("0xc").is_real()); + CHECK_UNARY(csubstr("0Xc").is_real()); + CHECK_UNARY(csubstr("0xd").is_real()); + CHECK_UNARY(csubstr("0Xd").is_real()); + CHECK_UNARY(csubstr("0xe").is_real()); + CHECK_UNARY(csubstr("0Xe").is_real()); + CHECK_UNARY(csubstr("0xf").is_real()); + CHECK_UNARY(csubstr("0Xf").is_real()); + CHECK_UNARY(csubstr("0xA").is_real()); + CHECK_UNARY(csubstr("0XA").is_real()); + CHECK_UNARY(csubstr("0xB").is_real()); + CHECK_UNARY(csubstr("0XB").is_real()); + CHECK_UNARY(csubstr("0xC").is_real()); + CHECK_UNARY(csubstr("0XC").is_real()); + CHECK_UNARY(csubstr("0xD").is_real()); + CHECK_UNARY(csubstr("0XD").is_real()); + CHECK_UNARY(csubstr("0xE").is_real()); + CHECK_UNARY(csubstr("0XE").is_real()); + CHECK_UNARY(csubstr("0xF").is_real()); + CHECK_UNARY(csubstr("0XF").is_real()); + CHECK_FALSE(csubstr("0xg").is_real()); + CHECK_FALSE(csubstr("0Xg").is_real()); + CHECK_FALSE(csubstr("0xG").is_real()); + CHECK_FALSE(csubstr("0XG").is_real()); + CHECK_UNARY(csubstr("-0x1").is_real()); + CHECK_UNARY(csubstr("-0X1").is_real()); + CHECK_UNARY(csubstr("-0x0").is_real()); + CHECK_UNARY(csubstr("-0X0").is_real()); + CHECK_UNARY(csubstr("-0xa").is_real()); + CHECK_UNARY(csubstr("-0Xa").is_real()); + CHECK_UNARY(csubstr("-0xb").is_real()); + CHECK_UNARY(csubstr("-0Xb").is_real()); + CHECK_UNARY(csubstr("-0xc").is_real()); + CHECK_UNARY(csubstr("-0Xc").is_real()); + CHECK_UNARY(csubstr("-0xd").is_real()); + CHECK_UNARY(csubstr("-0Xd").is_real()); + CHECK_UNARY(csubstr("-0xe").is_real()); + CHECK_UNARY(csubstr("-0Xe").is_real()); + CHECK_UNARY(csubstr("-0xf").is_real()); + CHECK_UNARY(csubstr("-0Xf").is_real()); + CHECK_UNARY(csubstr("-0xA").is_real()); + CHECK_UNARY(csubstr("-0XA").is_real()); + CHECK_UNARY(csubstr("-0xB").is_real()); + CHECK_UNARY(csubstr("-0XB").is_real()); + CHECK_UNARY(csubstr("-0xC").is_real()); + CHECK_UNARY(csubstr("-0XC").is_real()); + CHECK_UNARY(csubstr("-0xD").is_real()); + CHECK_UNARY(csubstr("-0XD").is_real()); + CHECK_UNARY(csubstr("-0xE").is_real()); + CHECK_UNARY(csubstr("-0XE").is_real()); + CHECK_UNARY(csubstr("-0xF").is_real()); + CHECK_UNARY(csubstr("-0XF").is_real()); + CHECK_FALSE(csubstr("-0xg").is_real()); + CHECK_FALSE(csubstr("-0Xg").is_real()); + CHECK_FALSE(csubstr("-0xG").is_real()); + CHECK_FALSE(csubstr("-0XG").is_real()); + CHECK_UNARY(csubstr("0x1.e8480p+19").is_real()); + CHECK_UNARY(csubstr("0X1.e8480P+19").is_real()); + CHECK_UNARY(csubstr("0x1.e8480P+19").is_real()); + CHECK_UNARY(csubstr("0X1.e8480p+19").is_real()); + CHECK_UNARY(csubstr("-0x1.e8480p+19").is_real()); + CHECK_UNARY(csubstr("-0X1.e8480P+19").is_real()); + CHECK_UNARY(csubstr("-0x1.e8480P+19").is_real()); + CHECK_UNARY(csubstr("-0X1.e8480p+19").is_real()); + } + SUBCASE("binary") + { + CHECK_FALSE(csubstr("0b").is_real()); + CHECK_FALSE(csubstr("0B").is_real()); + CHECK_UNARY(csubstr("0b0").is_real()); + CHECK_UNARY(csubstr("0B0").is_real()); + CHECK_UNARY(csubstr("0b1").is_real()); + CHECK_UNARY(csubstr("0B1").is_real()); + CHECK_FALSE(csubstr("0b2").is_real()); + CHECK_FALSE(csubstr("0B2").is_real()); + CHECK_UNARY(csubstr("-0b0").is_real()); + CHECK_UNARY(csubstr("-0B0").is_real()); + CHECK_UNARY(csubstr("-0b1").is_real()); + CHECK_UNARY(csubstr("-0B1").is_real()); + CHECK_FALSE(csubstr("-0b2").is_real()); + CHECK_FALSE(csubstr("-0B2").is_real()); + } + SUBCASE("octal") + { + CHECK_FALSE(csubstr("0o").is_real()); + CHECK_FALSE(csubstr("0O").is_real()); + CHECK_UNARY(csubstr("0o0").is_real()); + CHECK_UNARY(csubstr("0O0").is_real()); + CHECK_UNARY(csubstr("0o1").is_real()); + CHECK_UNARY(csubstr("0O1").is_real()); + CHECK_UNARY(csubstr("0o6").is_real()); + CHECK_UNARY(csubstr("0O6").is_real()); + CHECK_UNARY(csubstr("0o6").is_real()); + CHECK_UNARY(csubstr("0O6").is_real()); + CHECK_UNARY(csubstr("0o7").is_real()); + CHECK_UNARY(csubstr("0O7").is_real()); + CHECK_FALSE(csubstr("0o8").is_real()); + CHECK_FALSE(csubstr("0O8").is_real()); + CHECK_FALSE(csubstr("0o9").is_real()); + CHECK_FALSE(csubstr("0O9").is_real()); + CHECK_UNARY(csubstr("-0o0").is_real()); + CHECK_UNARY(csubstr("-0O0").is_real()); + CHECK_UNARY(csubstr("-0o1").is_real()); + CHECK_UNARY(csubstr("-0O1").is_real()); + CHECK_UNARY(csubstr("-0o6").is_real()); + CHECK_UNARY(csubstr("-0O6").is_real()); + CHECK_UNARY(csubstr("-0o6").is_real()); + CHECK_UNARY(csubstr("-0O6").is_real()); + CHECK_UNARY(csubstr("-0o7").is_real()); + CHECK_UNARY(csubstr("-0O7").is_real()); + CHECK_FALSE(csubstr("-0o8").is_real()); + CHECK_FALSE(csubstr("-0O8").is_real()); + CHECK_FALSE(csubstr("-0o9").is_real()); + CHECK_FALSE(csubstr("-0O9").is_real()); + } + SUBCASE("infinity") + { + CHECK_UNARY(csubstr("infinity").is_real()); + CHECK_FALSE(csubstr(" infinity").is_real()); + CHECK_UNARY(csubstr("-infinity").is_real()); + CHECK_FALSE(csubstr(" -infinity").is_real()); + CHECK_UNARY(csubstr("+infinity").is_real()); + CHECK_FALSE(csubstr(" +infinity").is_real()); + CHECK_FALSE(csubstr("infinity ").is_real()); + CHECK_FALSE(csubstr(" infinity ").is_real()); + CHECK_FALSE(csubstr("-infinity ").is_real()); + CHECK_FALSE(csubstr(" -infinity ").is_real()); + CHECK_FALSE(csubstr("+infinity ").is_real()); + CHECK_FALSE(csubstr(" +infinity ").is_real()); + CHECK_FALSE(csubstr("infinity1").is_real()); + CHECK_FALSE(csubstr(" infinity1").is_real()); + CHECK_FALSE(csubstr("-infinity1").is_real()); + CHECK_FALSE(csubstr(" -infinity1").is_real()); + CHECK_FALSE(csubstr("+infinity1").is_real()); + CHECK_FALSE(csubstr(" +infinity1").is_real()); + CHECK_FALSE(csubstr("infin").is_real()); + CHECK_FALSE(csubstr(" infin").is_real()); + CHECK_FALSE(csubstr("-infin").is_real()); + CHECK_FALSE(csubstr(" -infin").is_real()); + CHECK_FALSE(csubstr("+infin").is_real()); + CHECK_FALSE(csubstr(" +infin").is_real()); + CHECK_FALSE(csubstr("inflated").is_real()); + CHECK_FALSE(csubstr(" inflated").is_real()); + CHECK_FALSE(csubstr("-inflated").is_real()); + CHECK_FALSE(csubstr(" -inflated").is_real()); + CHECK_FALSE(csubstr("+inflated").is_real()); + CHECK_FALSE(csubstr(" +inflated").is_real()); + } + SUBCASE("inf") + { + CHECK_UNARY(csubstr("inf").is_real()); + CHECK_FALSE(csubstr(" inf").is_real()); + CHECK_UNARY(csubstr("-inf").is_real()); + CHECK_FALSE(csubstr(" -inf").is_real()); + CHECK_UNARY(csubstr("+inf").is_real()); + CHECK_FALSE(csubstr(" +inf").is_real()); + CHECK_FALSE(csubstr("inf ").is_real()); + CHECK_FALSE(csubstr(" inf ").is_real()); + CHECK_FALSE(csubstr("-inf ").is_real()); + CHECK_FALSE(csubstr(" -inf ").is_real()); + CHECK_FALSE(csubstr("+inf ").is_real()); + CHECK_FALSE(csubstr(" +inf ").is_real()); + CHECK_FALSE(csubstr("inf1").is_real()); + CHECK_FALSE(csubstr(" inf1").is_real()); + CHECK_FALSE(csubstr("-inf1").is_real()); + CHECK_FALSE(csubstr(" -inf1").is_real()); + CHECK_FALSE(csubstr("+inf1").is_real()); + CHECK_FALSE(csubstr(" +inf1").is_real()); + } + SUBCASE("nan") + { + CHECK_UNARY(csubstr("nan").is_real()); + CHECK_FALSE(csubstr(" nan").is_real()); + CHECK_UNARY(csubstr("-nan").is_real()); + CHECK_FALSE(csubstr(" -nan").is_real()); + CHECK_UNARY(csubstr("+nan").is_real()); + CHECK_FALSE(csubstr(" +nan").is_real()); + CHECK_FALSE(csubstr("nan ").is_real()); + CHECK_FALSE(csubstr(" nan ").is_real()); + CHECK_FALSE(csubstr("-nan ").is_real()); + CHECK_FALSE(csubstr(" -nan ").is_real()); + CHECK_FALSE(csubstr("+nan ").is_real()); + CHECK_FALSE(csubstr(" +nan ").is_real()); + CHECK_FALSE(csubstr("nan1").is_real()); + CHECK_FALSE(csubstr(" nan1").is_real()); + CHECK_FALSE(csubstr("-nan1").is_real()); + CHECK_FALSE(csubstr(" -nan1").is_real()); + CHECK_FALSE(csubstr("+nan1").is_real()); + CHECK_FALSE(csubstr(" +nan1").is_real()); + } +} + +typedef enum : uint8_t { kIsNone = 0, kIsUint = 1, kIsInt = 3, kIsReal = 7 } NumberClass; +struct number +{ + csubstr num; + NumberClass cls; + + template<size_t N> + number(const char (&n)[N], NumberClass c) : num(n), cls(c) {} + number(csubstr n, NumberClass c) : num(n), cls(c) {} + + void test(csubstr ref={}) + { + if(ref.empty()) + ref = num; + INFO("num=" << num); + INFO("ref=" << ref); + switch(cls) + { + case kIsUint: + { + INFO("uint"); + CHECK_EQ(num.first_uint_span(), ref); + CHECK_EQ(num.first_int_span(), ref); + CHECK_EQ(num.first_real_span(), ref); + CHECK_UNARY(num.first_uint_span().is_unsigned_integer()); + CHECK_UNARY(num.first_uint_span().is_integer()); + CHECK_UNARY(num.first_uint_span().is_number()); + break; + } + case kIsInt: + { + INFO("int"); + CHECK_EQ(num.first_uint_span(), ""); + CHECK_EQ(num.first_int_span(), ref); + CHECK_EQ(num.first_real_span(), ref); + CHECK_FALSE(num.first_int_span().is_unsigned_integer()); + CHECK_UNARY(num.first_int_span().is_integer()); + CHECK_UNARY(num.first_int_span().is_number()); + break; + } + case kIsReal: + { + INFO("real"); + CHECK_EQ(num.first_uint_span(), ""); + CHECK_EQ(num.first_int_span(), ""); + CHECK_EQ(num.first_real_span(), ref); + CHECK_FALSE(num.first_real_span().is_unsigned_integer()); + CHECK_FALSE(num.first_real_span().is_integer()); + CHECK_UNARY(num .first_real_span().is_number()); + break; + } + case kIsNone: + { + INFO("none"); + CHECK_EQ(num.first_uint_span(), ""); + CHECK_EQ(num.first_int_span(), ""); + CHECK_EQ(num.first_real_span(), ""); + CHECK_FALSE(num.is_unsigned_integer()); + CHECK_FALSE(num.is_integer()); + CHECK_FALSE(num.is_number()); + break; + } + default: + { + CHECK_UNARY(false);//FAIL(); + break; + } + } + } +}; + +const number numbers[] = { + {"", kIsNone}, + {"0x", kIsNone}, + {"0b", kIsNone}, + {"0o", kIsNone}, + {".", kIsNone}, + {"0x.", kIsNone}, + {"0b.", kIsNone}, + {"0o.", kIsNone}, + {"-", kIsNone}, + {"0x-", kIsNone}, + {"0b-", kIsNone}, + {"0o-", kIsNone}, + {"+", kIsNone}, + {"0x+", kIsNone}, + {"0b+", kIsNone}, + {"0o+", kIsNone}, + {".e", kIsNone}, + {".e+", kIsNone}, + {".e-", kIsNone}, + {"0x.p", kIsNone}, + {"0x.p+", kIsNone}, + {"0x.p-", kIsNone}, + {"0b.p", kIsNone}, + {"0b.p+", kIsNone}, + {"0b.p-", kIsNone}, + {"0o.p", kIsNone}, + {"0o.p+", kIsNone}, + {"0o.p-", kIsNone}, + {"0x.p+", kIsNone}, + {"0x0.p+", kIsNone}, + {"0x.0p+", kIsNone}, + {"0x.p+0", kIsNone}, + {"0x.p+00", kIsNone}, + {"0x0.0p+", kIsNone}, + {"0x.0p+0", kIsReal}, + {"0x0.p+0", kIsReal}, + {"0x0.0p+0", kIsReal}, + {"0x0.0p+00", kIsReal}, + {"0x00.00p+00", kIsReal}, + {"0x0p0.00p+00", kIsNone}, + {"0x00.0p0p+00", kIsNone}, + {"0x00.00p+0p0", kIsNone}, + {"0x00.00p+00p", kIsNone}, + {"0b.p+", kIsNone}, + {"0b0.p+", kIsNone}, + {"0b.0p+", kIsNone}, + {"0b.p+0", kIsNone}, + {"0b.p+00", kIsNone}, + {"0b0.0p+", kIsNone}, + {"0b.0p+0", kIsReal}, + {"0b0.p+0", kIsReal}, + {"0b0.0p+0", kIsReal}, + {"0b0.0p+00", kIsReal}, + {"0b00.00p+00", kIsReal}, + {"0b0p0.00p+00", kIsNone}, + {"0b00.0p0p+00", kIsNone}, + {"0b00.00p+0p0", kIsNone}, + {"0b00.00p+00p", kIsNone}, + {"0o.p+", kIsNone}, + {"0o0.p+", kIsNone}, + {"0o.0p+", kIsNone}, + {"0o.p+0", kIsNone}, + {"0o.p+00", kIsNone}, + {"0o0.0p+", kIsNone}, + {"0o.0p+0", kIsReal}, + {"0o0.p+0", kIsReal}, + {"0o0.0p+0", kIsReal}, + {"0o0.0p+00", kIsReal}, + {"0o00.00p+00", kIsReal}, + {"0o0p0.00p+00", kIsNone}, + {"0o00.0p0p+00", kIsNone}, + {"0o00.00p+0p0", kIsNone}, + {"0o00.00p+00p", kIsNone}, + {".e+", kIsNone}, + {"0.e+", kIsNone}, + {".0e+", kIsNone}, + {".e+0", kIsNone}, + {".e+00", kIsNone}, + {"0.0e+", kIsNone}, + {".0e+0", kIsReal}, + {"0.e+0", kIsReal}, + {"0.0e+0", kIsReal}, + {"0.0e+00", kIsReal}, + {"00.00e+00", kIsReal}, + {"0e0.00e+00", kIsNone}, + {"00.0e0e+00", kIsNone}, + {"00.00e+0e0", kIsNone}, + {"00.00e+00e", kIsNone}, + {"0x1234.", kIsReal}, + {"+0x1234.", kIsReal}, + {"-0x1234.", kIsReal}, + {"0x.1234", kIsReal}, + {"0x0.1.2.3", kIsNone}, + {"0o0.1.2.3", kIsNone}, + {"0b0.1.0.1", kIsNone}, + {"a", kIsNone}, + {"b", kIsNone}, + {"?", kIsNone}, + {"0.0.1", kIsNone}, + {"0.0.9", kIsNone}, + {"0.0.10", kIsNone}, + {"0.1.0", kIsNone}, + {"0.9.0", kIsNone}, + {"0.10.0", kIsNone}, + {"1.0.0", kIsNone}, + {"9.0.0", kIsNone}, + {"10.0.0", kIsNone}, + {".0", kIsReal}, + {"0.", kIsReal}, + {"0.0", kIsReal}, + {"1234", kIsUint}, + {"+1234", kIsUint}, + {"-1234", kIsInt}, + {"1234.0", kIsReal}, + {"+1234.0", kIsReal}, + {"-1234.0", kIsReal}, + {"0000", kIsUint}, + {"0123", kIsUint}, + {"0", kIsUint}, + {"1", kIsUint}, + {"1.", kIsReal}, + {".1", kIsReal}, + {"0x1234", kIsUint}, + {"+0x1234", kIsUint}, + {"-0x1234", kIsInt}, + {"0b01", kIsUint}, + {"1e+1", kIsReal}, + {"1e-1", kIsReal}, + {"1.e-1", kIsReal}, + {"1.e+1", kIsReal}, + {"1.0e-1", kIsReal}, + {"1.0e+1", kIsReal}, + {"1e+123", kIsReal}, + {"1e-123", kIsReal}, + {"1.e-123", kIsReal}, + {"1.e+123", kIsReal}, + {"1.0e123", kIsReal}, + {"1.0e-123", kIsReal}, + {"1.0e+123", kIsReal}, + {"0x1.e8480p+19", kIsReal}, + {"0x1.g8480p+19", kIsNone}, + {"0xg.e8480p+19", kIsNone}, + {"0b101.011p+19", kIsReal}, + {"0b101.012p+19", kIsNone}, + {"0b102.011p+19", kIsNone}, + {"0o173.045p+19", kIsReal}, + {"0o173.048p+19", kIsNone}, + {"0o178.045p+19", kIsNone}, + {"infinity", kIsReal}, + {"inf", kIsReal}, + {"nan", kIsReal}, +}; + +TEST_CASE("substr.is_number") +{ + SUBCASE("basic.hex") + { + CHECK_EQ(csubstr("0x.0p+0").first_real_span(), csubstr("0x.0p+0")); + CHECK_EQ(csubstr("0x)sdkjhsdfkju").first_int_span(), csubstr{}); + CHECK_EQ(csubstr("0x)sdkjhsdfkju").first_uint_span(), csubstr{}); + CHECK_EQ(csubstr("0x)sdkjhsdfkju").first_real_span(), csubstr{}); + CHECK_EQ(csubstr("0x0)sdkjhsdfkju").first_int_span(), csubstr("0x0")); + CHECK_EQ(csubstr("0x0)sdkjhsdfkju").first_uint_span(), csubstr("0x0")); + CHECK_EQ(csubstr("0x0)sdkjhsdfkju").first_real_span(), csubstr("0x0")); + } + SUBCASE("basic.dec") + { + CHECK_EQ(csubstr("+infinity").first_real_span(), csubstr("+infinity")); + CHECK_EQ(csubstr("-infinity").first_real_span(), csubstr("-infinity")); + CHECK_EQ(csubstr("+inf").first_real_span(), csubstr("+inf")); + CHECK_EQ(csubstr("-inf").first_real_span(), csubstr("-inf")); + CHECK_EQ(csubstr("0.e+0").first_real_span(), csubstr("0.e+0")); + CHECK_EQ(csubstr("0.e-0").first_real_span(), csubstr("0.e-0")); + } + SUBCASE("plain") + { + for(number n : numbers) + { + n.test(); + } + } + char buf[128]; + SUBCASE("leading+") + { + for(number n : numbers) + { + INFO("orig=" << n.num); + substr withplus = cat_sub(buf, '+', n.num); + NumberClass cls = n.cls; + if(withplus.begins_with("+-") || withplus.begins_with("++")) + cls = kIsNone; + number cp(withplus, cls); + cp.test(); + } + } + SUBCASE("leading-") + { + for(number n : numbers) + { + INFO("orig=" << n.num); + substr withminus = cat_sub(buf, '-', n.num); + NumberClass cls = n.cls; + if(cls == kIsUint) + cls = kIsInt; + if(withminus.begins_with("--") || withminus.begins_with("-+")) + cls = kIsNone; + number cp(withminus, cls); + cp.test(); + } + } + SUBCASE("capital_e") + { + for(number n : numbers) + { + INFO("orig=" << n.num); + substr replaced = cat_sub(buf, n.num); + replaced.replace('e', 'E'); + number cp(replaced, n.cls); + cp.test(); + } + } + SUBCASE("capital_p") + { + for(number n : numbers) + { + INFO("orig=" << n.num); + substr replaced = cat_sub(buf, n.num); + replaced.replace('p', 'P'); + number cp(replaced, n.cls); + cp.test(); + } + } + SUBCASE("capital_0x") + { + for(number n : numbers) + { + INFO("orig=" << n.num); + substr replaced = cat_sub(buf, n.num); + replaced.replace('x', 'X'); + number cp(replaced, n.cls); + cp.test(); + } + } + SUBCASE("capital_0b") + { + for(number n : numbers) + { + INFO("orig=" << n.num); + substr replaced = cat_sub(buf, n.num); + replaced.replace('b', 'B'); + number cp(replaced, n.cls); + cp.test(); + } + } + SUBCASE("capital_0o") + { + for(number n : numbers) + { + INFO("orig=" << n.num); + substr replaced = cat_sub(buf, n.num); + replaced.replace('o', 'O'); + number cp(replaced, n.cls); + cp.test(); + } + } + SUBCASE("numbers before") + { + char numbuf_[16] = {}; + substr numbuf = numbuf_; + for(number n : numbers) + { + INFO("orig=" << n.num); + for(char c : {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}) + { + numbuf.fill(c); + substr result = cat_sub(buf, numbuf, n.num); + number cp(result.sub(numbuf.len), n.cls); + cp.test(); + } + } + } + SUBCASE("numbers after") + { + char numbuf_[16] = {}; + substr numbuf = numbuf_; + for(number n : numbers) + { + INFO("orig=" << n.num); + for(char c : {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}) + { + numbuf.fill(c); + substr result = cat_sub(buf, n.num, numbuf); + number cp(result.first(n.num.len), n.cls); + cp.test(); + } + } + } + SUBCASE("delimiter after") + { + for(number n : numbers) + { + INFO("orig=" << n.num); + for(char c : {' ', '\n', ']', ')', '}', ',', ';', '\r', '\t','\0'}) + { + INFO("delimiter='" << c << "'"); + substr result = cat_sub(buf, n.num, c); + number cp(result, n.cls); + cp.test(n.num); + } + } + } + csubstr garbage = "sdkjhsdfkju"; + SUBCASE("prepend") + { + // adding anything before the number will make it not be a number + for(number n : numbers) + { + if(n.num.empty()) + continue; + INFO("orig=" << n.num); + for(int i = 0; i < 127; ++i) + { + char c = (char)i; + csubstr fmtd = cat_sub(buf, garbage, c, n.num); + number cp(fmtd, kIsNone); + cp.test(); + } + } + } + SUBCASE("append") + { + // adding after may or may not make it a number + for(number const& n : numbers) + { + INFO("orig=" << n.num); + for(int i = 0; i < 127; ++i) + { + number cp = n; + char c = (char)i; + cp.num = cat_sub(buf, n.num, c, garbage); + if(!csubstr::_is_delim_char(c)) + { + cp.cls = kIsNone; + } + cp.test(n.num); + } + } + } +} + +TEST_CASE("substr.triml") +{ + using S = csubstr; + + CHECK_EQ(S("aaabbb" ).triml('a' ), "bbb"); + CHECK_EQ(S("aaabbb" ).triml('b' ), "aaabbb"); + CHECK_EQ(S("aaabbb" ).triml('c' ), "aaabbb"); + CHECK_EQ(S("aaabbb" ).triml("ab"), ""); + CHECK_EQ(S("aaabbb" ).triml("ba"), ""); + CHECK_EQ(S("aaabbb" ).triml("cd"), "aaabbb"); + CHECK_EQ(S("aaa...bbb").triml('a' ), "...bbb"); + CHECK_EQ(S("aaa...bbb").triml('b' ), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").triml('c' ), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").triml("ab"), "...bbb"); + CHECK_EQ(S("aaa...bbb").triml("ba"), "...bbb"); + CHECK_EQ(S("aaa...bbb").triml("ab."), ""); + CHECK_EQ(S("aaa...bbb").triml("a."), "bbb"); + CHECK_EQ(S("aaa...bbb").triml(".a"), "bbb"); + CHECK_EQ(S("aaa...bbb").triml("b."), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").triml(".b"), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").triml("cd"), "aaa...bbb"); + + CHECK_EQ(S("ab" ).triml('a' ), "b"); + CHECK_EQ(S("ab" ).triml('b' ), "ab"); + CHECK_EQ(S("ab" ).triml('c' ), "ab"); + CHECK_EQ(S("ab" ).triml("ab"), ""); + CHECK_EQ(S("ab" ).triml("ba"), ""); + CHECK_EQ(S("ab" ).triml("cd"), "ab"); + CHECK_EQ(S("a...b").triml('a' ), "...b"); + CHECK_EQ(S("a...b").triml('b' ), "a...b"); + CHECK_EQ(S("a...b").triml('c' ), "a...b"); + CHECK_EQ(S("a...b").triml("ab"), "...b"); + CHECK_EQ(S("a...b").triml("ba"), "...b"); + CHECK_EQ(S("a...b").triml("ab."), ""); + CHECK_EQ(S("a...b").triml("a."), "b"); + CHECK_EQ(S("a...b").triml(".a"), "b"); + CHECK_EQ(S("a...b").triml("b."), "a...b"); + CHECK_EQ(S("a...b").triml(".b"), "a...b"); + CHECK_EQ(S("a...b").triml("cd"), "a...b"); +} + +TEST_CASE("substr.trimr") +{ + using S = csubstr; + + CHECK_EQ(S("aaabbb" ).trimr('a' ), "aaabbb"); + CHECK_EQ(S("aaabbb" ).trimr('b' ), "aaa"); + CHECK_EQ(S("aaabbb" ).trimr('c' ), "aaabbb"); + CHECK_EQ(S("aaabbb" ).trimr("ab"), ""); + CHECK_EQ(S("aaabbb" ).trimr("ba"), ""); + CHECK_EQ(S("aaabbb" ).trimr("cd"), "aaabbb"); + CHECK_EQ(S("aaa...bbb").trimr('a' ), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").trimr('b' ), "aaa..."); + CHECK_EQ(S("aaa...bbb").trimr('c' ), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").trimr("ab"), "aaa..."); + CHECK_EQ(S("aaa...bbb").trimr("ba"), "aaa..."); + CHECK_EQ(S("aaa...bbb").trimr("ab."), ""); + CHECK_EQ(S("aaa...bbb").trimr("a."), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").trimr(".a"), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").trimr("b."), "aaa"); + CHECK_EQ(S("aaa...bbb").trimr(".b"), "aaa"); + CHECK_EQ(S("aaa...bbb").trimr("cd"), "aaa...bbb"); + + CHECK_EQ(S("ab" ).trimr('a' ), "ab"); + CHECK_EQ(S("ab" ).trimr('b' ), "a"); + CHECK_EQ(S("ab" ).trimr('c' ), "ab"); + CHECK_EQ(S("ab" ).trimr("ab"), ""); + CHECK_EQ(S("ab" ).trimr("ba"), ""); + CHECK_EQ(S("ab" ).trimr("cd"), "ab"); + CHECK_EQ(S("a...b").trimr('a' ), "a...b"); + CHECK_EQ(S("a...b").trimr('b' ), "a..."); + CHECK_EQ(S("a...b").trimr('c' ), "a...b"); + CHECK_EQ(S("a...b").trimr("ab"), "a..."); + CHECK_EQ(S("a...b").trimr("ba"), "a..."); + CHECK_EQ(S("a...b").trimr("ab."), ""); + CHECK_EQ(S("a...b").trimr("a."), "a...b"); + CHECK_EQ(S("a...b").trimr(".a"), "a...b"); + CHECK_EQ(S("a...b").trimr("b."), "a"); + CHECK_EQ(S("a...b").trimr(".b"), "a"); + CHECK_EQ(S("a...b").trimr("cd"), "a...b"); +} + +TEST_CASE("substr.trim") +{ + using S = csubstr; + + CHECK_EQ(S("aaabbb" ).trim('a' ), "bbb"); + CHECK_EQ(S("aaabbb" ).trim('b' ), "aaa"); + CHECK_EQ(S("aaabbb" ).trim('c' ), "aaabbb"); + CHECK_EQ(S("aaabbb" ).trim("ab"), ""); + CHECK_EQ(S("aaabbb" ).trim("ba"), ""); + CHECK_EQ(S("aaabbb" ).trim("cd"), "aaabbb"); + CHECK_EQ(S("aaa...bbb").trim('a' ), "...bbb"); + CHECK_EQ(S("aaa...bbb").trim('b' ), "aaa..."); + CHECK_EQ(S("aaa...bbb").trim('c' ), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").trim("ab"), "..."); + CHECK_EQ(S("aaa...bbb").trim("ba"), "..."); + CHECK_EQ(S("aaa...bbb").trim('c' ), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").trim("ab."), ""); + CHECK_EQ(S("aaa...bbb").trim("." ), "aaa...bbb"); + CHECK_EQ(S("aaa...bbb").trim("a."), "bbb"); + CHECK_EQ(S("aaa...bbb").trim(".a"), "bbb"); + CHECK_EQ(S("aaa...bbb").trim("b."), "aaa"); + CHECK_EQ(S("aaa...bbb").trim(".b"), "aaa"); + CHECK_EQ(S("aaa...bbb").trim("cd"), "aaa...bbb"); + + CHECK_EQ(S("ab" ).trim('a' ), "b"); + CHECK_EQ(S("ab" ).trim('b' ), "a"); + CHECK_EQ(S("ab" ).trim('c' ), "ab"); + CHECK_EQ(S("ab" ).trim("ab"), ""); + CHECK_EQ(S("ab" ).trim("ba"), ""); + CHECK_EQ(S("ab" ).trim("cd"), "ab"); + CHECK_EQ(S("a...b").trim('a' ), "...b"); + CHECK_EQ(S("a...b").trim('b' ), "a..."); + CHECK_EQ(S("a...b").trim('c' ), "a...b"); + CHECK_EQ(S("a...b").trim("ab"), "..."); + CHECK_EQ(S("a...b").trim("ba"), "..."); + CHECK_EQ(S("a...b").trim('c' ), "a...b"); + CHECK_EQ(S("a...b").trim("ab."), ""); + CHECK_EQ(S("a...b").trim("." ), "a...b"); + CHECK_EQ(S("a...b").trim("a."), "b"); + CHECK_EQ(S("a...b").trim(".a"), "b"); + CHECK_EQ(S("a...b").trim("b."), "a"); + CHECK_EQ(S("a...b").trim(".b"), "a"); + CHECK_EQ(S("a...b").trim("cd"), "a...b"); +} + +TEST_CASE("substr.pop_right") +{ + using S = csubstr; + + CHECK_EQ(S("0/1/2" ).pop_right('/' ), "2"); + CHECK_EQ(S("0/1/2" ).pop_right('/', true), "2"); + CHECK_EQ(S("0/1/2/" ).pop_right('/' ), ""); + CHECK_EQ(S("0/1/2/" ).pop_right('/', true), "2/"); + CHECK_EQ(S("0/1/2///" ).pop_right('/' ), ""); + CHECK_EQ(S("0/1/2///" ).pop_right('/', true), "2///"); + + CHECK_EQ(S("0/1//2" ).pop_right('/' ), "2"); + CHECK_EQ(S("0/1//2" ).pop_right('/', true), "2"); + CHECK_EQ(S("0/1//2/" ).pop_right('/' ), ""); + CHECK_EQ(S("0/1//2/" ).pop_right('/', true), "2/"); + CHECK_EQ(S("0/1//2///" ).pop_right('/' ), ""); + CHECK_EQ(S("0/1//2///" ).pop_right('/', true), "2///"); + + CHECK_EQ(S("0/1///2" ).pop_right('/' ), "2"); + CHECK_EQ(S("0/1///2" ).pop_right('/', true), "2"); + CHECK_EQ(S("0/1///2/" ).pop_right('/' ), ""); + CHECK_EQ(S("0/1///2/" ).pop_right('/', true), "2/"); + CHECK_EQ(S("0/1///2///" ).pop_right('/' ), ""); + CHECK_EQ(S("0/1///2///" ).pop_right('/', true), "2///"); + + CHECK_EQ(S("/0/1/2" ).pop_right('/' ), "2"); + CHECK_EQ(S("/0/1/2" ).pop_right('/', true), "2"); + CHECK_EQ(S("/0/1/2/" ).pop_right('/' ), ""); + CHECK_EQ(S("/0/1/2/" ).pop_right('/', true), "2/"); + CHECK_EQ(S("/0/1/2///").pop_right('/' ), ""); + CHECK_EQ(S("/0/1/2///").pop_right('/', true), "2///"); + + CHECK_EQ(S("0" ).pop_right('/' ), "0"); + CHECK_EQ(S("0" ).pop_right('/', true), "0"); + CHECK_EQ(S("0/" ).pop_right('/' ), ""); + CHECK_EQ(S("0/" ).pop_right('/', true), "0/"); + CHECK_EQ(S("0///" ).pop_right('/' ), ""); + CHECK_EQ(S("0///" ).pop_right('/', true), "0///"); + + CHECK_EQ(S("/0" ).pop_right('/' ), "0"); + CHECK_EQ(S("/0" ).pop_right('/', true), "0"); + CHECK_EQ(S("/0/" ).pop_right('/' ), ""); + CHECK_EQ(S("/0/" ).pop_right('/', true), "0/"); + CHECK_EQ(S("/0///" ).pop_right('/' ), ""); + CHECK_EQ(S("/0///" ).pop_right('/', true), "0///"); + + CHECK_EQ(S("/" ).pop_right('/' ), ""); + CHECK_EQ(S("/" ).pop_right('/', true), ""); + CHECK_EQ(S("///" ).pop_right('/' ), ""); + CHECK_EQ(S("///" ).pop_right('/', true), ""); + + CHECK_EQ(S("" ).pop_right('/' ), ""); + CHECK_EQ(S("" ).pop_right('/', true), ""); + + CHECK_EQ(S("0-1-2" ).pop_right('-' ), "2"); + CHECK_EQ(S("0-1-2" ).pop_right('-', true), "2"); + CHECK_EQ(S("0-1-2-" ).pop_right('-' ), ""); + CHECK_EQ(S("0-1-2-" ).pop_right('-', true), "2-"); + CHECK_EQ(S("0-1-2---" ).pop_right('-' ), ""); + CHECK_EQ(S("0-1-2---" ).pop_right('-', true), "2---"); + + CHECK_EQ(S("0-1--2" ).pop_right('-' ), "2"); + CHECK_EQ(S("0-1--2" ).pop_right('-', true), "2"); + CHECK_EQ(S("0-1--2-" ).pop_right('-' ), ""); + CHECK_EQ(S("0-1--2-" ).pop_right('-', true), "2-"); + CHECK_EQ(S("0-1--2---" ).pop_right('-' ), ""); + CHECK_EQ(S("0-1--2---" ).pop_right('-', true), "2---"); + + CHECK_EQ(S("0-1---2" ).pop_right('-' ), "2"); + CHECK_EQ(S("0-1---2" ).pop_right('-', true), "2"); + CHECK_EQ(S("0-1---2-" ).pop_right('-' ), ""); + CHECK_EQ(S("0-1---2-" ).pop_right('-', true), "2-"); + CHECK_EQ(S("0-1---2---" ).pop_right('-' ), ""); + CHECK_EQ(S("0-1---2---" ).pop_right('-', true), "2---"); + + CHECK_EQ(S("-0-1-2" ).pop_right('-' ), "2"); + CHECK_EQ(S("-0-1-2" ).pop_right('-', true), "2"); + CHECK_EQ(S("-0-1-2-" ).pop_right('-' ), ""); + CHECK_EQ(S("-0-1-2-" ).pop_right('-', true), "2-"); + CHECK_EQ(S("-0-1-2---").pop_right('-' ), ""); + CHECK_EQ(S("-0-1-2---").pop_right('-', true), "2---"); + + CHECK_EQ(S("0" ).pop_right('-' ), "0"); + CHECK_EQ(S("0" ).pop_right('-', true), "0"); + CHECK_EQ(S("0-" ).pop_right('-' ), ""); + CHECK_EQ(S("0-" ).pop_right('-', true), "0-"); + CHECK_EQ(S("0---" ).pop_right('-' ), ""); + CHECK_EQ(S("0---" ).pop_right('-', true), "0---"); + + CHECK_EQ(S("-0" ).pop_right('-' ), "0"); + CHECK_EQ(S("-0" ).pop_right('-', true), "0"); + CHECK_EQ(S("-0-" ).pop_right('-' ), ""); + CHECK_EQ(S("-0-" ).pop_right('-', true), "0-"); + CHECK_EQ(S("-0---" ).pop_right('-' ), ""); + CHECK_EQ(S("-0---" ).pop_right('-', true), "0---"); + + CHECK_EQ(S("-" ).pop_right('-' ), ""); + CHECK_EQ(S("-" ).pop_right('-', true), ""); + CHECK_EQ(S("---" ).pop_right('-' ), ""); + CHECK_EQ(S("---" ).pop_right('-', true), ""); + + CHECK_EQ(S("" ).pop_right('-' ), ""); + CHECK_EQ(S("" ).pop_right('-', true), ""); +} + +TEST_CASE("substr.pop_left") +{ + using S = csubstr; + + CHECK_EQ(S("0/1/2" ).pop_left('/' ), "0"); + CHECK_EQ(S("0/1/2" ).pop_left('/', true), "0"); + CHECK_EQ(S("0/1/2/" ).pop_left('/' ), "0"); + CHECK_EQ(S("0/1/2/" ).pop_left('/', true), "0"); + CHECK_EQ(S("0/1/2///" ).pop_left('/' ), "0"); + CHECK_EQ(S("0/1/2///" ).pop_left('/', true), "0"); + + CHECK_EQ(S("0//1/2" ).pop_left('/' ), "0"); + CHECK_EQ(S("0//1/2" ).pop_left('/', true), "0"); + CHECK_EQ(S("0//1/2/" ).pop_left('/' ), "0"); + CHECK_EQ(S("0//1/2/" ).pop_left('/', true), "0"); + CHECK_EQ(S("0//1/2///" ).pop_left('/' ), "0"); + CHECK_EQ(S("0//1/2///" ).pop_left('/', true), "0"); + + CHECK_EQ(S("0///1/2" ).pop_left('/' ), "0"); + CHECK_EQ(S("0///1/2" ).pop_left('/', true), "0"); + CHECK_EQ(S("0///1/2/" ).pop_left('/' ), "0"); + CHECK_EQ(S("0///1/2/" ).pop_left('/', true), "0"); + CHECK_EQ(S("0///1/2///" ).pop_left('/' ), "0"); + CHECK_EQ(S("0///1/2///" ).pop_left('/', true), "0"); + + CHECK_EQ(S("/0/1/2" ).pop_left('/' ), ""); + CHECK_EQ(S("/0/1/2" ).pop_left('/', true), "/0"); + CHECK_EQ(S("/0/1/2/" ).pop_left('/' ), ""); + CHECK_EQ(S("/0/1/2/" ).pop_left('/', true), "/0"); + CHECK_EQ(S("/0/1/2///").pop_left('/' ), ""); + CHECK_EQ(S("/0/1/2///").pop_left('/', true), "/0"); + CHECK_EQ(S("///0/1/2" ).pop_left('/' ), ""); + CHECK_EQ(S("///0/1/2" ).pop_left('/', true), "///0"); + CHECK_EQ(S("///0/1/2/").pop_left('/' ), ""); + CHECK_EQ(S("///0/1/2/").pop_left('/', true), "///0"); + CHECK_EQ(S("///0/1/2/").pop_left('/' ), ""); + CHECK_EQ(S("///0/1/2/").pop_left('/', true), "///0"); + + CHECK_EQ(S("0" ).pop_left('/' ), "0"); + CHECK_EQ(S("0" ).pop_left('/', true), "0"); + CHECK_EQ(S("0/" ).pop_left('/' ), "0"); + CHECK_EQ(S("0/" ).pop_left('/', true), "0"); + CHECK_EQ(S("0///" ).pop_left('/' ), "0"); + CHECK_EQ(S("0///" ).pop_left('/', true), "0"); + + CHECK_EQ(S("/0" ).pop_left('/' ), ""); + CHECK_EQ(S("/0" ).pop_left('/', true), "/0"); + CHECK_EQ(S("/0/" ).pop_left('/' ), ""); + CHECK_EQ(S("/0/" ).pop_left('/', true), "/0"); + CHECK_EQ(S("/0///" ).pop_left('/' ), ""); + CHECK_EQ(S("/0///" ).pop_left('/', true), "/0"); + CHECK_EQ(S("///0///" ).pop_left('/' ), ""); + CHECK_EQ(S("///0///" ).pop_left('/', true), "///0"); + + CHECK_EQ(S("/" ).pop_left('/' ), ""); + CHECK_EQ(S("/" ).pop_left('/', true), ""); + CHECK_EQ(S("///" ).pop_left('/' ), ""); + CHECK_EQ(S("///" ).pop_left('/', true), ""); + + CHECK_EQ(S("" ).pop_left('/' ), ""); + CHECK_EQ(S("" ).pop_left('/', true), ""); + + CHECK_EQ(S("0-1-2" ).pop_left('-' ), "0"); + CHECK_EQ(S("0-1-2" ).pop_left('-', true), "0"); + CHECK_EQ(S("0-1-2-" ).pop_left('-' ), "0"); + CHECK_EQ(S("0-1-2-" ).pop_left('-', true), "0"); + CHECK_EQ(S("0-1-2---" ).pop_left('-' ), "0"); + CHECK_EQ(S("0-1-2---" ).pop_left('-', true), "0"); + + CHECK_EQ(S("0--1-2" ).pop_left('-' ), "0"); + CHECK_EQ(S("0--1-2" ).pop_left('-', true), "0"); + CHECK_EQ(S("0--1-2-" ).pop_left('-' ), "0"); + CHECK_EQ(S("0--1-2-" ).pop_left('-', true), "0"); + CHECK_EQ(S("0--1-2---" ).pop_left('-' ), "0"); + CHECK_EQ(S("0--1-2---" ).pop_left('-', true), "0"); + + CHECK_EQ(S("0---1-2" ).pop_left('-' ), "0"); + CHECK_EQ(S("0---1-2" ).pop_left('-', true), "0"); + CHECK_EQ(S("0---1-2-" ).pop_left('-' ), "0"); + CHECK_EQ(S("0---1-2-" ).pop_left('-', true), "0"); + CHECK_EQ(S("0---1-2---" ).pop_left('-' ), "0"); + CHECK_EQ(S("0---1-2---" ).pop_left('-', true), "0"); + + CHECK_EQ(S("-0-1-2" ).pop_left('-' ), ""); + CHECK_EQ(S("-0-1-2" ).pop_left('-', true), "-0"); + CHECK_EQ(S("-0-1-2-" ).pop_left('-' ), ""); + CHECK_EQ(S("-0-1-2-" ).pop_left('-', true), "-0"); + CHECK_EQ(S("-0-1-2---").pop_left('-' ), ""); + CHECK_EQ(S("-0-1-2---").pop_left('-', true), "-0"); + CHECK_EQ(S("---0-1-2" ).pop_left('-' ), ""); + CHECK_EQ(S("---0-1-2" ).pop_left('-', true), "---0"); + CHECK_EQ(S("---0-1-2-").pop_left('-' ), ""); + CHECK_EQ(S("---0-1-2-").pop_left('-', true), "---0"); + CHECK_EQ(S("---0-1-2-").pop_left('-' ), ""); + CHECK_EQ(S("---0-1-2-").pop_left('-', true), "---0"); + + CHECK_EQ(S("0" ).pop_left('-' ), "0"); + CHECK_EQ(S("0" ).pop_left('-', true), "0"); + CHECK_EQ(S("0-" ).pop_left('-' ), "0"); + CHECK_EQ(S("0-" ).pop_left('-', true), "0"); + CHECK_EQ(S("0---" ).pop_left('-' ), "0"); + CHECK_EQ(S("0---" ).pop_left('-', true), "0"); + + CHECK_EQ(S("-0" ).pop_left('-' ), ""); + CHECK_EQ(S("-0" ).pop_left('-', true), "-0"); + CHECK_EQ(S("-0-" ).pop_left('-' ), ""); + CHECK_EQ(S("-0-" ).pop_left('-', true), "-0"); + CHECK_EQ(S("-0---" ).pop_left('-' ), ""); + CHECK_EQ(S("-0---" ).pop_left('-', true), "-0"); + CHECK_EQ(S("---0---" ).pop_left('-' ), ""); + CHECK_EQ(S("---0---" ).pop_left('-', true), "---0"); + + CHECK_EQ(S("-" ).pop_left('-' ), ""); + CHECK_EQ(S("-" ).pop_left('-', true), ""); + CHECK_EQ(S("---" ).pop_left('-' ), ""); + CHECK_EQ(S("---" ).pop_left('-', true), ""); + + CHECK_EQ(S("" ).pop_left('-' ), ""); + CHECK_EQ(S("" ).pop_left('-', true), ""); +} + +TEST_CASE("substr.gpop_left") +{ + using S = csubstr; + + CHECK_EQ(S("0/1/2" ).gpop_left('/' ), "0/1"); + CHECK_EQ(S("0/1/2" ).gpop_left('/', true), "0/1"); + CHECK_EQ(S("0/1/2/" ).gpop_left('/' ), "0/1/2"); + CHECK_EQ(S("0/1/2/" ).gpop_left('/', true), "0/1"); + CHECK_EQ(S("0/1/2//" ).gpop_left('/' ), "0/1/2/"); + CHECK_EQ(S("0/1/2//" ).gpop_left('/', true), "0/1"); + CHECK_EQ(S("0/1/2///" ).gpop_left('/' ), "0/1/2//"); + CHECK_EQ(S("0/1/2///" ).gpop_left('/', true), "0/1"); + + CHECK_EQ(S("0/1//2" ).gpop_left('/' ), "0/1/"); + CHECK_EQ(S("0/1//2" ).gpop_left('/', true), "0/1"); + CHECK_EQ(S("0/1//2/" ).gpop_left('/' ), "0/1//2"); + CHECK_EQ(S("0/1//2/" ).gpop_left('/', true), "0/1"); + CHECK_EQ(S("0/1//2//" ).gpop_left('/' ), "0/1//2/"); + CHECK_EQ(S("0/1//2//" ).gpop_left('/', true), "0/1"); + CHECK_EQ(S("0/1//2///" ).gpop_left('/' ), "0/1//2//"); + CHECK_EQ(S("0/1//2///" ).gpop_left('/', true), "0/1"); + + CHECK_EQ(S("0/1///2" ).gpop_left('/' ), "0/1//"); + CHECK_EQ(S("0/1///2" ).gpop_left('/', true), "0/1"); + CHECK_EQ(S("0/1///2/" ).gpop_left('/' ), "0/1///2"); + CHECK_EQ(S("0/1///2/" ).gpop_left('/', true), "0/1"); + CHECK_EQ(S("0/1///2//" ).gpop_left('/' ), "0/1///2/"); + CHECK_EQ(S("0/1///2//" ).gpop_left('/', true), "0/1"); + CHECK_EQ(S("0/1///2///" ).gpop_left('/' ), "0/1///2//"); + CHECK_EQ(S("0/1///2///" ).gpop_left('/', true), "0/1"); + + CHECK_EQ(S("/0/1/2" ).gpop_left('/' ), "/0/1"); + CHECK_EQ(S("/0/1/2" ).gpop_left('/', true), "/0/1"); + CHECK_EQ(S("/0/1/2/" ).gpop_left('/' ), "/0/1/2"); + CHECK_EQ(S("/0/1/2/" ).gpop_left('/', true), "/0/1"); + CHECK_EQ(S("/0/1/2//" ).gpop_left('/' ), "/0/1/2/"); + CHECK_EQ(S("/0/1/2//" ).gpop_left('/', true), "/0/1"); + CHECK_EQ(S("/0/1/2///" ).gpop_left('/' ), "/0/1/2//"); + CHECK_EQ(S("/0/1/2///" ).gpop_left('/', true), "/0/1"); + + CHECK_EQ(S("//0/1/2" ).gpop_left('/' ), "//0/1"); + CHECK_EQ(S("//0/1/2" ).gpop_left('/', true), "//0/1"); + CHECK_EQ(S("//0/1/2/" ).gpop_left('/' ), "//0/1/2"); + CHECK_EQ(S("//0/1/2/" ).gpop_left('/', true), "//0/1"); + CHECK_EQ(S("//0/1/2//" ).gpop_left('/' ), "//0/1/2/"); + CHECK_EQ(S("//0/1/2//" ).gpop_left('/', true), "//0/1"); + CHECK_EQ(S("//0/1/2///" ).gpop_left('/' ), "//0/1/2//"); + CHECK_EQ(S("//0/1/2///" ).gpop_left('/', true), "//0/1"); + + CHECK_EQ(S("///0/1/2" ).gpop_left('/' ), "///0/1"); + CHECK_EQ(S("///0/1/2" ).gpop_left('/', true), "///0/1"); + CHECK_EQ(S("///0/1/2/" ).gpop_left('/' ), "///0/1/2"); + CHECK_EQ(S("///0/1/2/" ).gpop_left('/', true), "///0/1"); + CHECK_EQ(S("///0/1/2//" ).gpop_left('/' ), "///0/1/2/"); + CHECK_EQ(S("///0/1/2//" ).gpop_left('/', true), "///0/1"); + CHECK_EQ(S("///0/1/2///").gpop_left('/' ), "///0/1/2//"); + CHECK_EQ(S("///0/1/2///").gpop_left('/', true), "///0/1"); + + + CHECK_EQ(S("0/1" ).gpop_left('/' ), "0"); + CHECK_EQ(S("0/1" ).gpop_left('/', true), "0"); + CHECK_EQ(S("0/1/" ).gpop_left('/' ), "0/1"); + CHECK_EQ(S("0/1/" ).gpop_left('/', true), "0"); + CHECK_EQ(S("0/1//" ).gpop_left('/' ), "0/1/"); + CHECK_EQ(S("0/1//" ).gpop_left('/', true), "0"); + CHECK_EQ(S("0/1///" ).gpop_left('/' ), "0/1//"); + CHECK_EQ(S("0/1///" ).gpop_left('/', true), "0"); + + CHECK_EQ(S("0//1" ).gpop_left('/' ), "0/"); + CHECK_EQ(S("0//1" ).gpop_left('/', true), "0"); + CHECK_EQ(S("0//1/" ).gpop_left('/' ), "0//1"); + CHECK_EQ(S("0//1/" ).gpop_left('/', true), "0"); + CHECK_EQ(S("0//1//" ).gpop_left('/' ), "0//1/"); + CHECK_EQ(S("0//1//" ).gpop_left('/', true), "0"); + CHECK_EQ(S("0//1///" ).gpop_left('/' ), "0//1//"); + CHECK_EQ(S("0//1///" ).gpop_left('/', true), "0"); + + CHECK_EQ(S("0///1" ).gpop_left('/' ), "0//"); + CHECK_EQ(S("0///1" ).gpop_left('/', true), "0"); + CHECK_EQ(S("0///1/" ).gpop_left('/' ), "0///1"); + CHECK_EQ(S("0///1/" ).gpop_left('/', true), "0"); + CHECK_EQ(S("0///1//" ).gpop_left('/' ), "0///1/"); + CHECK_EQ(S("0///1//" ).gpop_left('/', true), "0"); + CHECK_EQ(S("0///1///" ).gpop_left('/' ), "0///1//"); + CHECK_EQ(S("0///1///" ).gpop_left('/', true), "0"); + + CHECK_EQ(S("/0/1" ).gpop_left('/' ), "/0"); + CHECK_EQ(S("/0/1" ).gpop_left('/', true), "/0"); + CHECK_EQ(S("/0/1/" ).gpop_left('/' ), "/0/1"); + CHECK_EQ(S("/0/1/" ).gpop_left('/', true), "/0"); + CHECK_EQ(S("/0/1//" ).gpop_left('/' ), "/0/1/"); + CHECK_EQ(S("/0/1//" ).gpop_left('/', true), "/0"); + CHECK_EQ(S("/0/1///" ).gpop_left('/' ), "/0/1//"); + CHECK_EQ(S("/0/1///" ).gpop_left('/', true), "/0"); + + CHECK_EQ(S("/0//1" ).gpop_left('/' ), "/0/"); + CHECK_EQ(S("/0//1" ).gpop_left('/', true), "/0"); + CHECK_EQ(S("/0//1/" ).gpop_left('/' ), "/0//1"); + CHECK_EQ(S("/0//1/" ).gpop_left('/', true), "/0"); + CHECK_EQ(S("/0//1//" ).gpop_left('/' ), "/0//1/"); + CHECK_EQ(S("/0//1//" ).gpop_left('/', true), "/0"); + CHECK_EQ(S("/0//1///" ).gpop_left('/' ), "/0//1//"); + CHECK_EQ(S("/0//1///" ).gpop_left('/', true), "/0"); + + CHECK_EQ(S("/0///1" ).gpop_left('/' ), "/0//"); + CHECK_EQ(S("/0///1" ).gpop_left('/', true), "/0"); + CHECK_EQ(S("/0///1/" ).gpop_left('/' ), "/0///1"); + CHECK_EQ(S("/0///1/" ).gpop_left('/', true), "/0"); + CHECK_EQ(S("/0///1//" ).gpop_left('/' ), "/0///1/"); + CHECK_EQ(S("/0///1//" ).gpop_left('/', true), "/0"); + CHECK_EQ(S("/0///1///" ).gpop_left('/' ), "/0///1//"); + CHECK_EQ(S("/0///1///" ).gpop_left('/', true), "/0"); + + CHECK_EQ(S("//0/1" ).gpop_left('/' ), "//0"); + CHECK_EQ(S("//0/1" ).gpop_left('/', true), "//0"); + CHECK_EQ(S("//0/1/" ).gpop_left('/' ), "//0/1"); + CHECK_EQ(S("//0/1/" ).gpop_left('/', true), "//0"); + CHECK_EQ(S("//0/1//" ).gpop_left('/' ), "//0/1/"); + CHECK_EQ(S("//0/1//" ).gpop_left('/', true), "//0"); + CHECK_EQ(S("//0/1///" ).gpop_left('/' ), "//0/1//"); + CHECK_EQ(S("//0/1///" ).gpop_left('/', true), "//0"); + + CHECK_EQ(S("//0//1" ).gpop_left('/' ), "//0/"); + CHECK_EQ(S("//0//1" ).gpop_left('/', true), "//0"); + CHECK_EQ(S("//0//1/" ).gpop_left('/' ), "//0//1"); + CHECK_EQ(S("//0//1/" ).gpop_left('/', true), "//0"); + CHECK_EQ(S("//0//1//" ).gpop_left('/' ), "//0//1/"); + CHECK_EQ(S("//0//1//" ).gpop_left('/', true), "//0"); + CHECK_EQ(S("//0//1///" ).gpop_left('/' ), "//0//1//"); + CHECK_EQ(S("//0//1///" ).gpop_left('/', true), "//0"); + + CHECK_EQ(S("//0///1" ).gpop_left('/' ), "//0//"); + CHECK_EQ(S("//0///1" ).gpop_left('/', true), "//0"); + CHECK_EQ(S("//0///1/" ).gpop_left('/' ), "//0///1"); + CHECK_EQ(S("//0///1/" ).gpop_left('/', true), "//0"); + CHECK_EQ(S("//0///1//" ).gpop_left('/' ), "//0///1/"); + CHECK_EQ(S("//0///1//" ).gpop_left('/', true), "//0"); + CHECK_EQ(S("//0///1///" ).gpop_left('/' ), "//0///1//"); + CHECK_EQ(S("//0///1///" ).gpop_left('/', true), "//0"); + + CHECK_EQ(S("0" ).gpop_left('/' ), ""); + CHECK_EQ(S("0" ).gpop_left('/', true), ""); + CHECK_EQ(S("0/" ).gpop_left('/' ), "0"); + CHECK_EQ(S("0/" ).gpop_left('/', true), ""); + CHECK_EQ(S("0//" ).gpop_left('/' ), "0/"); + CHECK_EQ(S("0//" ).gpop_left('/', true), ""); + CHECK_EQ(S("0///" ).gpop_left('/' ), "0//"); + CHECK_EQ(S("0///" ).gpop_left('/', true), ""); + + CHECK_EQ(S("/0" ).gpop_left('/' ), ""); + CHECK_EQ(S("/0" ).gpop_left('/', true), ""); + CHECK_EQ(S("/0/" ).gpop_left('/' ), "/0"); + CHECK_EQ(S("/0/" ).gpop_left('/', true), ""); + CHECK_EQ(S("/0//" ).gpop_left('/' ), "/0/"); + CHECK_EQ(S("/0//" ).gpop_left('/', true), ""); + CHECK_EQ(S("/0///" ).gpop_left('/' ), "/0//"); + CHECK_EQ(S("/0///" ).gpop_left('/', true), ""); + + CHECK_EQ(S("//0" ).gpop_left('/' ), "/"); + CHECK_EQ(S("//0" ).gpop_left('/', true), ""); + CHECK_EQ(S("//0/" ).gpop_left('/' ), "//0"); + CHECK_EQ(S("//0/" ).gpop_left('/', true), ""); + CHECK_EQ(S("//0//" ).gpop_left('/' ), "//0/"); + CHECK_EQ(S("//0//" ).gpop_left('/', true), ""); + CHECK_EQ(S("//0///" ).gpop_left('/' ), "//0//"); + CHECK_EQ(S("//0///" ).gpop_left('/', true), ""); + + CHECK_EQ(S("///0" ).gpop_left('/' ), "//"); + CHECK_EQ(S("///0" ).gpop_left('/', true), ""); + CHECK_EQ(S("///0/" ).gpop_left('/' ), "///0"); + CHECK_EQ(S("///0/" ).gpop_left('/', true), ""); + CHECK_EQ(S("///0//" ).gpop_left('/' ), "///0/"); + CHECK_EQ(S("///0//" ).gpop_left('/', true), ""); + CHECK_EQ(S("///0///" ).gpop_left('/' ), "///0//"); + CHECK_EQ(S("///0///" ).gpop_left('/', true), ""); + + CHECK_EQ(S("/" ).gpop_left('/' ), ""); + CHECK_EQ(S("/" ).gpop_left('/', true), ""); + CHECK_EQ(S("//" ).gpop_left('/' ), "/"); + CHECK_EQ(S("//" ).gpop_left('/', true), ""); + CHECK_EQ(S("///" ).gpop_left('/' ), "//"); + CHECK_EQ(S("///" ).gpop_left('/', true), ""); + + CHECK_EQ(S("" ).gpop_left('/' ), ""); + CHECK_EQ(S("" ).gpop_left('/', true), ""); +} + +TEST_CASE("substr.gpop_right") +{ + using S = csubstr; + + CHECK_EQ(S("0/1/2" ).gpop_right('/' ), "1/2"); + CHECK_EQ(S("0/1/2" ).gpop_right('/', true), "1/2"); + CHECK_EQ(S("0/1/2/" ).gpop_right('/' ), "1/2/"); + CHECK_EQ(S("0/1/2/" ).gpop_right('/', true), "1/2/"); + CHECK_EQ(S("0/1/2//" ).gpop_right('/' ), "1/2//"); + CHECK_EQ(S("0/1/2//" ).gpop_right('/', true), "1/2//"); + CHECK_EQ(S("0/1/2///" ).gpop_right('/' ), "1/2///"); + CHECK_EQ(S("0/1/2///" ).gpop_right('/', true), "1/2///"); + + CHECK_EQ(S("0//1/2" ).gpop_right('/' ), "/1/2"); + CHECK_EQ(S("0//1/2" ).gpop_right('/', true), "1/2"); + CHECK_EQ(S("0//1/2/" ).gpop_right('/' ), "/1/2/"); + CHECK_EQ(S("0//1/2/" ).gpop_right('/', true), "1/2/"); + CHECK_EQ(S("0//1/2//" ).gpop_right('/' ), "/1/2//"); + CHECK_EQ(S("0//1/2//" ).gpop_right('/', true), "1/2//"); + CHECK_EQ(S("0//1/2///" ).gpop_right('/' ), "/1/2///"); + CHECK_EQ(S("0//1/2///" ).gpop_right('/', true), "1/2///"); + + CHECK_EQ(S("0///1/2" ).gpop_right('/' ), "//1/2"); + CHECK_EQ(S("0///1/2" ).gpop_right('/', true), "1/2"); + CHECK_EQ(S("0///1/2/" ).gpop_right('/' ), "//1/2/"); + CHECK_EQ(S("0///1/2/" ).gpop_right('/', true), "1/2/"); + CHECK_EQ(S("0///1/2//" ).gpop_right('/' ), "//1/2//"); + CHECK_EQ(S("0///1/2//" ).gpop_right('/', true), "1/2//"); + CHECK_EQ(S("0///1/2///" ).gpop_right('/' ), "//1/2///"); + CHECK_EQ(S("0///1/2///" ).gpop_right('/', true), "1/2///"); + + + CHECK_EQ(S("/0/1/2" ).gpop_right('/' ), "0/1/2"); + CHECK_EQ(S("/0/1/2" ).gpop_right('/', true), "1/2"); + CHECK_EQ(S("/0/1/2/" ).gpop_right('/' ), "0/1/2/"); + CHECK_EQ(S("/0/1/2/" ).gpop_right('/', true), "1/2/"); + CHECK_EQ(S("/0/1/2//" ).gpop_right('/' ), "0/1/2//"); + CHECK_EQ(S("/0/1/2//" ).gpop_right('/', true), "1/2//"); + CHECK_EQ(S("/0/1/2///" ).gpop_right('/' ), "0/1/2///"); + CHECK_EQ(S("/0/1/2///" ).gpop_right('/', true), "1/2///"); + + CHECK_EQ(S("/0//1/2" ).gpop_right('/' ), "0//1/2"); + CHECK_EQ(S("/0//1/2" ).gpop_right('/', true), "1/2"); + CHECK_EQ(S("/0//1/2/" ).gpop_right('/' ), "0//1/2/"); + CHECK_EQ(S("/0//1/2/" ).gpop_right('/', true), "1/2/"); + CHECK_EQ(S("/0//1/2//" ).gpop_right('/' ), "0//1/2//"); + CHECK_EQ(S("/0//1/2//" ).gpop_right('/', true), "1/2//"); + CHECK_EQ(S("/0//1/2///" ).gpop_right('/' ), "0//1/2///"); + CHECK_EQ(S("/0//1/2///" ).gpop_right('/', true), "1/2///"); + + CHECK_EQ(S("/0///1/2" ).gpop_right('/' ), "0///1/2"); + CHECK_EQ(S("/0///1/2" ).gpop_right('/', true), "1/2"); + CHECK_EQ(S("/0///1/2/" ).gpop_right('/' ), "0///1/2/"); + CHECK_EQ(S("/0///1/2/" ).gpop_right('/', true), "1/2/"); + CHECK_EQ(S("/0///1/2//" ).gpop_right('/' ), "0///1/2//"); + CHECK_EQ(S("/0///1/2//" ).gpop_right('/', true), "1/2//"); + CHECK_EQ(S("/0///1/2///" ).gpop_right('/' ), "0///1/2///"); + CHECK_EQ(S("/0///1/2///" ).gpop_right('/', true), "1/2///"); + + + CHECK_EQ(S("//0/1/2" ).gpop_right('/' ), "/0/1/2"); + CHECK_EQ(S("//0/1/2" ).gpop_right('/', true), "1/2"); + CHECK_EQ(S("//0/1/2/" ).gpop_right('/' ), "/0/1/2/"); + CHECK_EQ(S("//0/1/2/" ).gpop_right('/', true), "1/2/"); + CHECK_EQ(S("//0/1/2//" ).gpop_right('/' ), "/0/1/2//"); + CHECK_EQ(S("//0/1/2//" ).gpop_right('/', true), "1/2//"); + CHECK_EQ(S("//0/1/2///" ).gpop_right('/' ), "/0/1/2///"); + CHECK_EQ(S("//0/1/2///" ).gpop_right('/', true), "1/2///"); + + CHECK_EQ(S("//0//1/2" ).gpop_right('/' ), "/0//1/2"); + CHECK_EQ(S("//0//1/2" ).gpop_right('/', true), "1/2"); + CHECK_EQ(S("//0//1/2/" ).gpop_right('/' ), "/0//1/2/"); + CHECK_EQ(S("//0//1/2/" ).gpop_right('/', true), "1/2/"); + CHECK_EQ(S("//0//1/2//" ).gpop_right('/' ), "/0//1/2//"); + CHECK_EQ(S("//0//1/2//" ).gpop_right('/', true), "1/2//"); + CHECK_EQ(S("//0//1/2///" ).gpop_right('/' ), "/0//1/2///"); + CHECK_EQ(S("//0//1/2///" ).gpop_right('/', true), "1/2///"); + + CHECK_EQ(S("//0///1/2" ).gpop_right('/' ), "/0///1/2"); + CHECK_EQ(S("//0///1/2" ).gpop_right('/', true), "1/2"); + CHECK_EQ(S("//0///1/2/" ).gpop_right('/' ), "/0///1/2/"); + CHECK_EQ(S("//0///1/2/" ).gpop_right('/', true), "1/2/"); + CHECK_EQ(S("//0///1/2//" ).gpop_right('/' ), "/0///1/2//"); + CHECK_EQ(S("//0///1/2//" ).gpop_right('/', true), "1/2//"); + CHECK_EQ(S("//0///1/2///" ).gpop_right('/' ), "/0///1/2///"); + CHECK_EQ(S("//0///1/2///" ).gpop_right('/', true), "1/2///"); + + + CHECK_EQ(S("0/1" ).gpop_right('/' ), "1"); + CHECK_EQ(S("0/1" ).gpop_right('/', true), "1"); + CHECK_EQ(S("0/1/" ).gpop_right('/' ), "1/"); + CHECK_EQ(S("0/1/" ).gpop_right('/', true), "1/"); + CHECK_EQ(S("0/1//" ).gpop_right('/' ), "1//"); + CHECK_EQ(S("0/1//" ).gpop_right('/', true), "1//"); + CHECK_EQ(S("0/1///" ).gpop_right('/' ), "1///"); + CHECK_EQ(S("0/1///" ).gpop_right('/', true), "1///"); + + CHECK_EQ(S("0//1" ).gpop_right('/' ), "/1"); + CHECK_EQ(S("0//1" ).gpop_right('/', true), "1"); + CHECK_EQ(S("0//1/" ).gpop_right('/' ), "/1/"); + CHECK_EQ(S("0//1/" ).gpop_right('/', true), "1/"); + CHECK_EQ(S("0//1//" ).gpop_right('/' ), "/1//"); + CHECK_EQ(S("0//1//" ).gpop_right('/', true), "1//"); + CHECK_EQ(S("0//1///" ).gpop_right('/' ), "/1///"); + CHECK_EQ(S("0//1///" ).gpop_right('/', true), "1///"); + + CHECK_EQ(S("0///1" ).gpop_right('/' ), "//1"); + CHECK_EQ(S("0///1" ).gpop_right('/', true), "1"); + CHECK_EQ(S("0///1/" ).gpop_right('/' ), "//1/"); + CHECK_EQ(S("0///1/" ).gpop_right('/', true), "1/"); + CHECK_EQ(S("0///1//" ).gpop_right('/' ), "//1//"); + CHECK_EQ(S("0///1//" ).gpop_right('/', true), "1//"); + CHECK_EQ(S("0///1///" ).gpop_right('/' ), "//1///"); + CHECK_EQ(S("0///1///" ).gpop_right('/', true), "1///"); + + + CHECK_EQ(S("/0/1" ).gpop_right('/' ), "0/1"); + CHECK_EQ(S("/0/1" ).gpop_right('/', true), "1"); + CHECK_EQ(S("/0/1/" ).gpop_right('/' ), "0/1/"); + CHECK_EQ(S("/0/1/" ).gpop_right('/', true), "1/"); + CHECK_EQ(S("/0/1//" ).gpop_right('/' ), "0/1//"); + CHECK_EQ(S("/0/1//" ).gpop_right('/', true), "1//"); + CHECK_EQ(S("/0/1///" ).gpop_right('/' ), "0/1///"); + CHECK_EQ(S("/0/1///" ).gpop_right('/', true), "1///"); + + CHECK_EQ(S("/0//1" ).gpop_right('/' ), "0//1"); + CHECK_EQ(S("/0//1" ).gpop_right('/', true), "1"); + CHECK_EQ(S("/0//1/" ).gpop_right('/' ), "0//1/"); + CHECK_EQ(S("/0//1/" ).gpop_right('/', true), "1/"); + CHECK_EQ(S("/0//1//" ).gpop_right('/' ), "0//1//"); + CHECK_EQ(S("/0//1//" ).gpop_right('/', true), "1//"); + CHECK_EQ(S("/0//1///" ).gpop_right('/' ), "0//1///"); + CHECK_EQ(S("/0//1///" ).gpop_right('/', true), "1///"); + + CHECK_EQ(S("/0///1" ).gpop_right('/' ), "0///1"); + CHECK_EQ(S("/0///1" ).gpop_right('/', true), "1"); + CHECK_EQ(S("/0///1/" ).gpop_right('/' ), "0///1/"); + CHECK_EQ(S("/0///1/" ).gpop_right('/', true), "1/"); + CHECK_EQ(S("/0///1//" ).gpop_right('/' ), "0///1//"); + CHECK_EQ(S("/0///1//" ).gpop_right('/', true), "1//"); + CHECK_EQ(S("/0///1///" ).gpop_right('/' ), "0///1///"); + CHECK_EQ(S("/0///1///" ).gpop_right('/', true), "1///"); + + + CHECK_EQ(S("//0/1" ).gpop_right('/' ), "/0/1"); + CHECK_EQ(S("//0/1" ).gpop_right('/', true), "1"); + CHECK_EQ(S("//0/1/" ).gpop_right('/' ), "/0/1/"); + CHECK_EQ(S("//0/1/" ).gpop_right('/', true), "1/"); + CHECK_EQ(S("//0/1//" ).gpop_right('/' ), "/0/1//"); + CHECK_EQ(S("//0/1//" ).gpop_right('/', true), "1//"); + CHECK_EQ(S("//0/1///" ).gpop_right('/' ), "/0/1///"); + CHECK_EQ(S("//0/1///" ).gpop_right('/', true), "1///"); + + CHECK_EQ(S("//0//1" ).gpop_right('/' ), "/0//1"); + CHECK_EQ(S("//0//1" ).gpop_right('/', true), "1"); + CHECK_EQ(S("//0//1/" ).gpop_right('/' ), "/0//1/"); + CHECK_EQ(S("//0//1/" ).gpop_right('/', true), "1/"); + CHECK_EQ(S("//0//1//" ).gpop_right('/' ), "/0//1//"); + CHECK_EQ(S("//0//1//" ).gpop_right('/', true), "1//"); + CHECK_EQ(S("//0//1///" ).gpop_right('/' ), "/0//1///"); + CHECK_EQ(S("//0//1///" ).gpop_right('/', true), "1///"); + + CHECK_EQ(S("//0///1" ).gpop_right('/' ), "/0///1"); + CHECK_EQ(S("//0///1" ).gpop_right('/', true), "1"); + CHECK_EQ(S("//0///1/" ).gpop_right('/' ), "/0///1/"); + CHECK_EQ(S("//0///1/" ).gpop_right('/', true), "1/"); + CHECK_EQ(S("//0///1//" ).gpop_right('/' ), "/0///1//"); + CHECK_EQ(S("//0///1//" ).gpop_right('/', true), "1//"); + CHECK_EQ(S("//0///1///" ).gpop_right('/' ), "/0///1///"); + CHECK_EQ(S("//0///1///" ).gpop_right('/', true), "1///"); + + + CHECK_EQ(S("0" ).gpop_right('/' ), ""); + CHECK_EQ(S("0" ).gpop_right('/', true), ""); + CHECK_EQ(S("0/" ).gpop_right('/' ), ""); + CHECK_EQ(S("0/" ).gpop_right('/', true), ""); + CHECK_EQ(S("0//" ).gpop_right('/' ), "/"); + CHECK_EQ(S("0//" ).gpop_right('/', true), ""); + CHECK_EQ(S("0///" ).gpop_right('/' ), "//"); + CHECK_EQ(S("0///" ).gpop_right('/', true), ""); + + CHECK_EQ(S("/0" ).gpop_right('/' ), "0"); + CHECK_EQ(S("/0" ).gpop_right('/', true), ""); + CHECK_EQ(S("/0/" ).gpop_right('/' ), "0/"); + CHECK_EQ(S("/0/" ).gpop_right('/', true), ""); + CHECK_EQ(S("/0//" ).gpop_right('/' ), "0//"); + CHECK_EQ(S("/0//" ).gpop_right('/', true), ""); + CHECK_EQ(S("/0///" ).gpop_right('/' ), "0///"); + CHECK_EQ(S("/0///" ).gpop_right('/', true), ""); + + CHECK_EQ(S("//0" ).gpop_right('/' ), "/0"); + CHECK_EQ(S("//0" ).gpop_right('/', true), ""); + CHECK_EQ(S("//0/" ).gpop_right('/' ), "/0/"); + CHECK_EQ(S("//0/" ).gpop_right('/', true), ""); + CHECK_EQ(S("//0//" ).gpop_right('/' ), "/0//"); + CHECK_EQ(S("//0//" ).gpop_right('/', true), ""); + CHECK_EQ(S("//0///" ).gpop_right('/' ), "/0///"); + CHECK_EQ(S("//0///" ).gpop_right('/', true), ""); + + CHECK_EQ(S("///0" ).gpop_right('/' ), "//0"); + CHECK_EQ(S("///0" ).gpop_right('/', true), ""); + CHECK_EQ(S("///0/" ).gpop_right('/' ), "//0/"); + CHECK_EQ(S("///0/" ).gpop_right('/', true), ""); + CHECK_EQ(S("///0//" ).gpop_right('/' ), "//0//"); + CHECK_EQ(S("///0//" ).gpop_right('/', true), ""); + CHECK_EQ(S("///0///" ).gpop_right('/' ), "//0///"); + CHECK_EQ(S("///0///" ).gpop_right('/', true), ""); + + CHECK_EQ(S("/" ).gpop_right('/' ), ""); + CHECK_EQ(S("/" ).gpop_right('/', true), ""); + CHECK_EQ(S("//" ).gpop_right('/' ), "/"); + CHECK_EQ(S("//" ).gpop_right('/', true), ""); + CHECK_EQ(S("///" ).gpop_right('/' ), "//"); + CHECK_EQ(S("///" ).gpop_right('/', true), ""); + + CHECK_EQ(S("" ).gpop_right('/' ), ""); + CHECK_EQ(S("" ).gpop_right('/', true), ""); +} + +TEST_CASE("substr.basename") +{ + using S = csubstr; + CHECK_EQ(S("0/1/2").basename(), "2"); + CHECK_EQ(S("0/1/2/").basename(), "2"); + CHECK_EQ(S("0/1/2///").basename(), "2"); + CHECK_EQ(S("/0/1/2").basename(), "2"); + CHECK_EQ(S("/0/1/2/").basename(), "2"); + CHECK_EQ(S("/0/1/2///").basename(), "2"); + CHECK_EQ(S("///0/1/2").basename(), "2"); + CHECK_EQ(S("///0/1/2/").basename(), "2"); + CHECK_EQ(S("///0/1/2///").basename(), "2"); + CHECK_EQ(S("/").basename(), ""); + CHECK_EQ(S("//").basename(), ""); + CHECK_EQ(S("///").basename(), ""); + CHECK_EQ(S("////").basename(), ""); + CHECK_EQ(S("").basename(), ""); +} + +TEST_CASE("substr.dirname") +{ + using S = csubstr; + CHECK_EQ(S("0/1/2").dirname(), "0/1/"); + CHECK_EQ(S("0/1/2/").dirname(), "0/1/"); + CHECK_EQ(S("/0/1/2").dirname(), "/0/1/"); + CHECK_EQ(S("/0/1/2/").dirname(), "/0/1/"); + CHECK_EQ(S("///0/1/2").dirname(), "///0/1/"); + CHECK_EQ(S("///0/1/2/").dirname(), "///0/1/"); + CHECK_EQ(S("/0").dirname(), "/"); + CHECK_EQ(S("/").dirname(), "/"); + CHECK_EQ(S("//").dirname(), "//"); + CHECK_EQ(S("///").dirname(), "///"); + CHECK_EQ(S("////").dirname(), "////"); + CHECK_EQ(S("").dirname(), ""); +} + +TEST_CASE("substr.extshort") +{ + using S = csubstr; + CHECK_EQ(S("filename.with.ext").extshort(), "ext"); + CHECK_EQ(S("filename.with.ext.").extshort(), ""); + CHECK_EQ(S(".a.b").extshort(), "b"); + CHECK_EQ(S(".a.b.").extshort(), ""); + CHECK_EQ(S(".b..").extshort(), ""); + CHECK_EQ(S("..b.").extshort(), ""); +} + +TEST_CASE("substr.extlong") +{ + using S = csubstr; + CHECK_EQ(S("filename.with.ext").extlong(), "with.ext"); + CHECK_EQ(S("filename.with.ext.").extlong(), "with.ext."); + CHECK_EQ(S(".a.b").extlong(), "a.b"); + CHECK_EQ(S(".a.b.").extlong(), "a.b."); + CHECK_EQ(S(".b..").extlong(), "b.."); + CHECK_EQ(S("..b.").extlong(), ".b."); +} + +TEST_CASE("substr.next_split") +{ + using S = csubstr; + + { + S const n; + typename S::size_type pos = 0; + S ss; + CHECK_EQ(n.next_split(':', &pos, &ss), false); + CHECK_EQ(ss.empty(), true); + CHECK_EQ(n.next_split(':', &pos, &ss), false); + CHECK_EQ(ss.empty(), true); + pos = 0; + CHECK_EQ(n.next_split(',', &pos, &ss), false); + CHECK_EQ(ss.empty(), true); + CHECK_EQ(n.next_split(',', &pos, &ss), false); + CHECK_EQ(ss.empty(), true); + } + + { + S const n("0"); + typename S::size_type pos = 0; + S ss; + CHECK_EQ(n.next_split(':', &pos, &ss), true); + CHECK_EQ(ss.empty(), false); + CHECK_EQ(n.next_split(':', &pos, &ss), false); + CHECK_EQ(ss.empty(), true); + CHECK_EQ(n.next_split(':', &pos, &ss), false); + CHECK_EQ(ss.empty(), true); + pos = 0; + CHECK_EQ(n.next_split(',', &pos, &ss), true); + CHECK_EQ(ss.empty(), false); + CHECK_EQ(n.next_split(',', &pos, &ss), false); + CHECK_EQ(ss.empty(), true); + CHECK_EQ(n.next_split(',', &pos, &ss), false); + CHECK_EQ(ss.empty(), true); + } + + { + S const n; + typename S::size_type pos = 0; + typename S::size_type count = 0; + S ss; + while(n.next_split(':', &pos, &ss)) + { + ++count; + } + CHECK_EQ(count, 0); + } + + { + S const n("0123456"); + typename S::size_type pos = 0; + typename S::size_type count = 0; + S ss; + while(n.next_split(':', &pos, &ss)) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), n.size()); + CHECK_EQ(ss.empty(), false); + break; + default: + CHECK_UNARY(false);//GTEST_FAIL(); + break; + } + ++count; + } + CHECK_EQ(count, 1); + } + + { + S const n("0123456:"); + typename S::size_type pos = 0; + typename S::size_type count = 0; + S ss; + while(n.next_split(':', &pos, &ss)) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), n.size()-1); + CHECK_EQ(ss.empty(), false); + break; + case 1: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + default: + CHECK_UNARY(false);//GTEST_FAIL(); + break; + } + ++count; + } + CHECK_EQ(count, 2); + } + + { + S const n(":0123456:"); + typename S::size_type pos = 0; + typename S::size_type count = 0; + S ss; + while(n.next_split(':', &pos, &ss)) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + case 1: + CHECK_EQ(ss.size(), n.size()-2); + CHECK_EQ(ss.empty(), false); + break; + case 2: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + default: + CHECK_UNARY(false);//GTEST_FAIL(); + break; + } + ++count; + } + CHECK_EQ(count, 3); + } + + { + S const n(":"); + typename S::size_type pos = 0; + typename S::size_type count = 0; + S ss; + while(n.next_split(':', &pos, &ss)) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + case 1: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + default: + CHECK_UNARY(false);//GTEST_FAIL(); + break; + } + ++count; + } + CHECK_EQ(count, 2); + } + + { + S const n("01:23:45:67"); + typename S::size_type pos = 0; + typename S::size_type count = 0; + S ss; + while(n.next_split(':', &pos, &ss)) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "01"); + CHECK_NE(ss, "01:"); + CHECK_NE(ss, ":01:"); + CHECK_NE(ss, ":01"); + break; + case 1: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "23"); + CHECK_NE(ss, "23:"); + CHECK_NE(ss, ":23:"); + CHECK_NE(ss, ":23"); + break; + case 2: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "45"); + CHECK_NE(ss, "45:"); + CHECK_NE(ss, ":45:"); + CHECK_NE(ss, ":45"); + break; + case 3: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "67"); + CHECK_NE(ss, "67:"); + CHECK_NE(ss, ":67:"); + CHECK_NE(ss, ":67"); + break; + default: + CHECK_UNARY(false);//GTEST_FAIL(); + break; + } + count++; + } + CHECK_EQ(count, 4); + } + + { + const S n(":01:23:45:67:"); + typename S::size_type pos = 0; + typename S::size_type count = 0; + S ss; + while(n.next_split(':', &pos, &ss)) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + case 1: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "01"); + CHECK_NE(ss, "01:"); + CHECK_NE(ss, ":01:"); + CHECK_NE(ss, ":01"); + break; + case 2: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "23"); + CHECK_NE(ss, "23:"); + CHECK_NE(ss, ":23:"); + CHECK_NE(ss, ":23"); + break; + case 3: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "45"); + CHECK_NE(ss, "45:"); + CHECK_NE(ss, ":45:"); + CHECK_NE(ss, ":45"); + break; + case 4: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "67"); + CHECK_NE(ss, "67:"); + CHECK_NE(ss, ":67:"); + CHECK_NE(ss, ":67"); + break; + case 5: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + default: + CHECK_UNARY(false);//GTEST_FAIL(); + break; + } + count++; + } + CHECK_EQ(count, 6); + } + + { + const S n("::::01:23:45:67::::"); + typename S::size_type pos = 0; + typename S::size_type count = 0; + S ss; + while(n.next_split(':', &pos, &ss)) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 1: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 2: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 3: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 4: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "01"); + CHECK_NE(ss, "01:"); + CHECK_NE(ss, ":01:"); + CHECK_NE(ss, ":01"); + break; + case 5: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "23"); + CHECK_NE(ss, "23:"); + CHECK_NE(ss, ":23:"); + CHECK_NE(ss, ":23"); + break; + case 6: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "45"); + CHECK_NE(ss, "45:"); + CHECK_NE(ss, ":45:"); + CHECK_NE(ss, ":45"); + break; + case 7: + CHECK_EQ(ss.size(), 2); + CHECK_EQ(ss, "67"); + CHECK_NE(ss, "67:"); + CHECK_NE(ss, ":67:"); + CHECK_NE(ss, ":67"); + break; + case 8: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 9: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 10: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 11: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + default: + CHECK_UNARY(false);//GTEST_FAIL(); + break; + } + count++; + } + CHECK_EQ(count, 12); + } +} + +TEST_CASE("substr.split") +{ + using S = csubstr; + + { + S const n; + { + auto spl = n.split(':'); + auto beg = spl.begin(); + auto end = spl.end(); + CHECK_UNARY(beg == end); + } + } + + { + S const n("foo:bar:baz"); + auto spl = n.split(':'); + auto beg = spl.begin(); + auto end = spl.end(); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(end->size(), 0); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + auto it = beg; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "foo"); + CHECK_UNARY(it != end); + CHECK_UNARY(it == beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + ++it; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "bar"); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + ++it; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "baz"); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + ++it; + CHECK_EQ(it->size(), 0); + CHECK_UNARY(it == end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + it = beg; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "foo"); + CHECK_UNARY(it != end); + CHECK_UNARY(it == beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + it++; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "bar"); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + it++; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "baz"); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + it++; + CHECK_EQ(it->size(), 0); + CHECK_UNARY(it == end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + } + + { + S const n("foo:bar:baz:"); + auto spl = n.split(':'); + auto beg = spl.begin(); + auto end = spl.end(); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(end->size(), 0); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + auto it = beg; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "foo"); + CHECK_UNARY(it != end); + CHECK_UNARY(it == beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + ++it; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "bar"); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + ++it; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "baz"); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + ++it; + CHECK_EQ(it->size(), 0); + CHECK_EQ(*it, ""); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + ++it; + CHECK_EQ(it->size(), 0); + CHECK_UNARY(it == end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + //-------------------------- + it = beg; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "foo"); + CHECK_UNARY(it != end); + CHECK_UNARY(it == beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + it++; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "bar"); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + it++; + CHECK_EQ(it->size(), 3); + CHECK_EQ(*it, "baz"); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + it++; + CHECK_EQ(it->size(), 0); + CHECK_EQ(*it, ""); + CHECK_UNARY(it != end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + it++; + CHECK_EQ(it->size(), 0); + CHECK_UNARY(it == end); + CHECK_UNARY(it != beg); + CHECK_EQ(beg->size(), 3); + CHECK_EQ(*beg, "foo"); + CHECK_UNARY(beg != end); + } + + { + S const n; + auto s = n.split(':'); + // check that multiple calls to begin() always yield the same result + CHECK_EQ(*s.begin(), ""); + CHECK_EQ(*s.begin(), ""); + CHECK_EQ(*s.begin(), ""); + // check that multiple calls to end() always yield the same result + auto e = s.end(); + CHECK_UNARY(s.end() == e); + CHECK_UNARY(s.end() == e); + // + auto it = s.begin(); + CHECK_EQ(*it, ""); + CHECK_EQ(it->empty(), true); + CHECK_EQ(it->size(), 0); + ++it; + CHECK_UNARY(it == e); + } + + { + S const n("01:23:45:67"); + auto s = n.split(':'); + // check that multiple calls to begin() always yield the same result + CHECK_EQ(*s.begin(), "01"); + CHECK_EQ(*s.begin(), "01"); + CHECK_EQ(*s.begin(), "01"); + // check that multiple calls to end() always yield the same result + auto e = s.end(); + CHECK_UNARY(s.end() == e); + CHECK_UNARY(s.end() == e); + CHECK_UNARY(s.end() == e); + // + auto it = s.begin(); + CHECK_EQ(*it, "01"); + CHECK_EQ(it->size(), 2); + ++it; + CHECK_EQ(*it, "23"); + CHECK_EQ(it->size(), 2); + ++it; + CHECK_EQ(*it, "45"); + CHECK_EQ(it->size(), 2); + ++it; + CHECK_EQ(*it, "67"); + CHECK_EQ(it->size(), 2); + ++it; + CHECK_UNARY(it == s.end()); + } + + { + S const n; + typename S::size_type count = 0; + for(auto &ss : n.split(':')) + { + ++count; + } + CHECK_EQ(count, 0); + } + + { + S const n("0123456"); + { + auto spl = n.split(':'); + auto beg = spl.begin(); + auto end = spl.end(); + CHECK_EQ(beg->size(), n.size()); + CHECK_EQ(end->size(), 0); + } + typename S::size_type count = 0; + for(auto &ss : n.split(':')) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), n.size()); + CHECK_EQ(ss.empty(), false); + break; + } + ++count; + } + CHECK_EQ(count, 1); + } + + { + S const n("foo:bar"); + typename S::size_type count = 0; + for(auto &ss : n.split(':')) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 3); + CHECK_EQ(ss.empty(), false); + CHECK_EQ(ss, "foo"); + break; + case 1: + CHECK_EQ(ss.size(), 3); + CHECK_EQ(ss.empty(), false); + CHECK_EQ(ss, "bar"); + break; + } + ++count; + } + CHECK_EQ(count, 2); + } + + { + S const n("0123456:"); + typename S::size_type count = 0; + for(auto &ss : n.split(':')) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), n.size()-1); + CHECK_EQ(ss.empty(), false); + break; + case 1: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + } + ++count; + } + CHECK_EQ(count, 2); + } + + { + S const n(":0123456:"); + typename S::size_type count = 0; + for(auto &ss : n.split(':')) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + case 1: + CHECK_EQ(ss.size(), n.size()-2); + CHECK_EQ(ss.empty(), false); + break; + case 2: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + } + ++count; + } + CHECK_EQ(count, 3); + } + + { + S const n(":"); + typename S::size_type count = 0; + for(auto &ss : n.split(':')) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + case 1: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + } + ++count; + } + CHECK_EQ(count, 2); + } + + { + S const n("01:23:45:67"); + typename S::size_type count = 0; + for(auto &ss : n.split(':')) + { + switch(count) + { + case 0: + CHECK_EQ(ss, "01"); + CHECK_NE(ss, "01:"); + CHECK_NE(ss, ":01:"); + CHECK_NE(ss, ":01"); + break; + case 1: + CHECK_EQ(ss, "23"); + CHECK_NE(ss, "23:"); + CHECK_NE(ss, ":23:"); + CHECK_NE(ss, ":23"); + break; + case 2: + CHECK_EQ(ss, "45"); + CHECK_NE(ss, "45:"); + CHECK_NE(ss, ":45:"); + CHECK_NE(ss, ":45"); + break; + case 3: + CHECK_EQ(ss, "67"); + CHECK_NE(ss, "67:"); + CHECK_NE(ss, ":67:"); + CHECK_NE(ss, ":67"); + break; + } + count++; + } + CHECK_EQ(count, 4); + } + + { + const S n(":01:23:45:67:"); + typename S::size_type count = 0; + for(auto &ss : n.split(':')) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + case 1: + CHECK_EQ(ss, "01"); + CHECK_NE(ss, "01:"); + CHECK_NE(ss, ":01:"); + CHECK_NE(ss, ":01"); + break; + case 2: + CHECK_EQ(ss, "23"); + CHECK_NE(ss, "23:"); + CHECK_NE(ss, ":23:"); + CHECK_NE(ss, ":23"); + break; + case 3: + CHECK_EQ(ss, "45"); + CHECK_NE(ss, "45:"); + CHECK_NE(ss, ":45:"); + CHECK_NE(ss, ":45"); + break; + case 4: + CHECK_EQ(ss, "67"); + CHECK_NE(ss, "67:"); + CHECK_NE(ss, ":67:"); + CHECK_NE(ss, ":67"); + break; + case 5: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + break; + } + count++; + } + CHECK_EQ(count, 6); + } + + { + const S n("::::01:23:45:67::::"); + typename S::size_type count = 0; + for(auto &ss : n.split(':')) + { + switch(count) + { + case 0: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 1: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 2: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 3: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 4: + CHECK_EQ(ss, "01"); + CHECK_NE(ss, "01:"); + CHECK_NE(ss, ":01:"); + CHECK_NE(ss, ":01"); + break; + case 5: + CHECK_EQ(ss, "23"); + CHECK_NE(ss, "23:"); + CHECK_NE(ss, ":23:"); + CHECK_NE(ss, ":23"); + break; + case 6: + CHECK_EQ(ss, "45"); + CHECK_NE(ss, "45:"); + CHECK_NE(ss, ":45:"); + CHECK_NE(ss, ":45"); + break; + case 7: + CHECK_EQ(ss, "67"); + CHECK_NE(ss, "67:"); + CHECK_NE(ss, ":67:"); + CHECK_NE(ss, ":67"); + break; + case 8: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 9: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 10: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + case 11: + CHECK_EQ(ss.size(), 0); + CHECK_EQ(ss.empty(), true); + CHECK_NE(ss, "::"); + break; + } + count++; + } + CHECK_EQ(count, 12); + } +} + + +//----------------------------------------------------------------------------- +TEST_CASE("substr.copy_from") +{ + char buf[128] = {0}; + substr s = buf; + CHECK_EQ(s.size(), sizeof(buf)-1); + CHECK_NE(s.first(3), "123"); + s.copy_from("123"); + CHECK_EQ(s.first(3), "123"); + CHECK_EQ(s.first(6), "123\0\0\0"); + s.copy_from("+++", 3); + CHECK_EQ(s.first(6), "123+++"); + CHECK_EQ(s.first(9), "123+++\0\0\0"); + s.copy_from("456", 6); + CHECK_EQ(s.first(9), "123+++456"); + CHECK_EQ(s.first(12), "123+++456\0\0\0"); + s.copy_from("***", 3); + CHECK_EQ(s.first(9), "123***456"); + CHECK_EQ(s.first(12), "123***456\0\0\0"); + + // make sure that it's safe to pass source strings that don't fit + // in the remaining destination space + substr ss = s.first(9); + ss.copy_from("987654321", 9); // should be a no-op + CHECK_EQ(s.first(12), "123***456\0\0\0"); + ss.copy_from("987654321", 6); + CHECK_EQ(s.first(12), "123***987\0\0\0"); + ss.copy_from("987654321", 3); + CHECK_EQ(s.first(12), "123987654\0\0\0"); + ss.first(3).copy_from("987654321"); + CHECK_EQ(s.first(12), "987987654\0\0\0"); +} + + +//----------------------------------------------------------------------------- +void do_test_reverse(substr s, csubstr orig, csubstr expected) +{ + CHECK_EQ(s, orig); + s.reverse(); + CHECK_EQ(s, expected); + s.reverse(); + CHECK_EQ(s, orig); + // + CHECK_EQ(s, orig); + s.reverse_sub(0, s.len); + CHECK_EQ(s, expected); + s.reverse_sub(0, s.len); + CHECK_EQ(s, orig); + // + CHECK_EQ(s, orig); + s.reverse_range(0, s.len); + CHECK_EQ(s, expected); + s.reverse_range(0, s.len); + CHECK_EQ(s, orig); +} + +TEST_CASE("substr.reverse") +{ + char buf[] = "0123456789"; + do_test_reverse(buf, "0123456789", "9876543210"); + do_test_reverse(buf, "0123456789", "9876543210"); + + // in the middle + substr s = buf; + s.sub(2, 2).reverse(); + CHECK_EQ(s, "0132456789"); + s.sub(2, 2).reverse(); + CHECK_EQ(s, "0123456789"); + + s.sub(4, 2).reverse(); + CHECK_EQ(s, "0123546789"); + s.sub(4, 2).reverse(); + CHECK_EQ(s, "0123456789"); + + // at the beginning + s.first(3).reverse(); + CHECK_EQ(s, "2103456789"); + s.first(3).reverse(); + CHECK_EQ(s, "0123456789"); + + // at the end + s.last(3).reverse(); + CHECK_EQ(s, "0123456987"); + s.last(3).reverse(); + CHECK_EQ(s, "0123456789"); +} + + +TEST_CASE("substr.reverse_sub") +{ + char buf[] = "0123456789"; + substr s = buf; + + s.reverse_sub(0, s.len); + CHECK_EQ(s, "9876543210"); + s.reverse_sub(0, s.len); + CHECK_EQ(s, "0123456789"); + + s.reverse_sub(0, 0); + CHECK_EQ(s, "0123456789"); + s.reverse_sub(s.len, 0); + CHECK_EQ(s, "0123456789"); + + s.reverse_sub(1, 3); + CHECK_EQ(s, "0321456789"); + s.reverse_sub(1, 3); + CHECK_EQ(s, "0123456789"); +} + +TEST_CASE("substr.reverse_range") +{ + char buf[] = "0123456789"; + substr s = buf; + + s.reverse_range(0, s.len); + CHECK_EQ(s, "9876543210"); + s.reverse_range(0, s.len); + CHECK_EQ(s, "0123456789"); + + s.reverse_range(0, 0); + CHECK_EQ(s, "0123456789"); + s.reverse_range(s.len, s.len); + CHECK_EQ(s, "0123456789"); + + s.reverse_range(1, 3); + CHECK_EQ(s, "0213456789"); + s.reverse_range(1, 3); + CHECK_EQ(s, "0123456789"); +} + + +//----------------------------------------------------------------------------- +TEST_CASE("substr.erase") +{ + char buf[] = "0123456789"; + + substr s = buf; + CHECK_EQ(s.len, s.size()); + CHECK_EQ(s.len, 10); + CHECK_EQ(s, "0123456789"); + + substr ss = s.first(6); + CHECK_EQ(ss.len, 6); + for(size_t i = 0; i <= ss.len; ++i) + { + ss.erase(i, 0); // must be a no-op + CHECK_EQ(s, "0123456789"); + ss.erase_range(i, i); // must be a no-op + CHECK_EQ(s, "0123456789"); + ss.erase(ss.len-i, i); // must be a no-op + CHECK_EQ(s, "0123456789"); + } + + substr r; + ss = ss.erase(0, 1); + CHECK_EQ(ss.len, 5); + CHECK_EQ(ss, "12345"); + CHECK_EQ(s, "1234556789"); + ss = ss.erase(0, 2); + CHECK_EQ(ss.len, 3); + CHECK_EQ(ss, "345"); + CHECK_EQ(s, "3454556789"); + + csubstr s55 = s.sub(4, 2); + ss = s.erase(s55); + CHECK_EQ(s, "3454678989"); +} + + +//----------------------------------------------------------------------------- +TEST_CASE("substr.replace") +{ + char buf[] = "0.1.2.3.4.5.6.7.8.9"; + + substr s = buf; + + auto ret = s.replace('+', '.'); + CHECK_EQ(ret, 0); + + ret = s.replace('.', '.', s.len); + CHECK_EQ(s, "0.1.2.3.4.5.6.7.8.9"); + CHECK_EQ(ret, 0); + ret = s.replace('.', '.'); + CHECK_EQ(ret, 9); + CHECK_EQ(s, "0.1.2.3.4.5.6.7.8.9"); + + ret = s.replace('.', '+', s.len); + CHECK_EQ(s, "0.1.2.3.4.5.6.7.8.9"); + CHECK_EQ(ret, 0); + ret = s.replace('.', '+'); + CHECK_EQ(ret, 9); + CHECK_EQ(s, "0+1+2+3+4+5+6+7+8+9"); + + ret = s.replace("16", '.', s.len); + CHECK_EQ(s, "0+1+2+3+4+5+6+7+8+9"); + CHECK_EQ(ret, 0); + ret = s.replace("16", '.'); + CHECK_EQ(ret, 2); + CHECK_EQ(s, "0+.+2+3+4+5+.+7+8+9"); + ret = s.replace("3+2", '_'); + CHECK_EQ(ret, 11); + CHECK_EQ(s, "0_._____4_5_._7_8_9"); + + // must accept empty string + ret = s.sub(0, 0).replace('0', '1'); + CHECK_EQ(ret, 0); + CHECK_EQ(s, "0_._____4_5_._7_8_9"); + ret = s.sub(0, 0).replace("0", '1'); + CHECK_EQ(ret, 0); + CHECK_EQ(s, "0_._____4_5_._7_8_9"); +} + +TEST_CASE("substr.replace_all") +{ + char buf[] = "0.1.2.3.4.5.6.7.8.9"; + std::string tmp, out("0+1+2+3+4+5+6+7+8+9"); + + // must accept empty string + substr(buf).sub(0, 0).replace_all(to_substr(tmp), "0", "X"); + CHECK_EQ(csubstr(buf), "0.1.2.3.4.5.6.7.8.9"); + + substr r; + auto replall = [&](csubstr pattern, csubstr repl) -> substr { + tmp = out; + csubstr rtmp = to_csubstr(tmp); + out.resize(128); + substr dst = to_substr(out); + size_t sz = rtmp.replace_all(dst, pattern, repl); + CHECK_LE(sz, out.size()); + out.resize(sz); + return dst.first(sz); + }; + r = replall("0+1", "0+++++1"); + // the result must be a view of out + CHECK_FALSE(r.empty()); + CHECK_FALSE(out.empty()); + CHECK_EQ(r.size(), out.size()); + CHECK_EQ(r.front(), out.front()); + CHECK_EQ(r.back(), out.back()); + CHECK_EQ(r, "0+++++1+2+3+4+5+6+7+8+9"); + + r = replall("+", ""); + CHECK_EQ(r, "0123456789"); + + r = replall("+", ""); + CHECK_EQ(r, "0123456789"); // must not change + + r = replall("0123456789", "9876543210"); + CHECK_EQ(r, "9876543210"); + + r = replall("987", "."); + CHECK_EQ(r, ".6543210"); + + r = replall("210", "."); + CHECK_EQ(r, ".6543."); + + r = replall("6543", ":"); + CHECK_EQ(r, ".:."); + + r = replall(".:.", ""); + CHECK_EQ(r, ""); +} + +TEST_CASE("substr.short_integer") +{ + char buf[] = "-"; + CHECK_FALSE(substr(buf).is_integer()); + CHECK_FALSE(csubstr("-").is_integer()); + CHECK_FALSE(csubstr("+").is_integer()); +} + +} // namespace c4 + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_szconv.cpp b/thirdparty/ryml/ext/c4core/test/test_szconv.cpp new file mode 100644 index 000000000..d54bf48a0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_szconv.cpp @@ -0,0 +1,166 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/szconv.hpp" +#endif + +#include "c4/libtest/supprwarn_push.hpp" +#include "c4/test.hpp" + +#define C4_EXPECT_NARROWER(yes_or_no, ty_out, ty_in) \ + CHECK_UNARY(( yes_or_no (is_narrower_size<ty_out C4_COMMA ty_in>::value))); + +namespace c4 { + +TEST_CASE("is_narrower_size.signed_types") +{ + C4_EXPECT_NARROWER( ! , int8_t , int8_t ); + C4_EXPECT_NARROWER( , int8_t , int16_t); + C4_EXPECT_NARROWER( , int8_t , int32_t); + C4_EXPECT_NARROWER( , int8_t , int64_t); + C4_EXPECT_NARROWER( , int8_t , uint8_t ); + C4_EXPECT_NARROWER( , int8_t , uint16_t); + C4_EXPECT_NARROWER( , int8_t , uint32_t); + C4_EXPECT_NARROWER( , int8_t , uint64_t); + C4_EXPECT_NARROWER( ! , int16_t , int8_t ); + C4_EXPECT_NARROWER( ! , int16_t , int16_t); + C4_EXPECT_NARROWER( , int16_t , int32_t); + C4_EXPECT_NARROWER( , int16_t , int64_t); + C4_EXPECT_NARROWER( ! , int16_t , uint8_t ); + C4_EXPECT_NARROWER( , int16_t , uint16_t); + C4_EXPECT_NARROWER( , int16_t , uint32_t); + C4_EXPECT_NARROWER( , int16_t , uint64_t); + C4_EXPECT_NARROWER( ! , int32_t , int8_t ); + C4_EXPECT_NARROWER( ! , int32_t , int16_t); + C4_EXPECT_NARROWER( ! , int32_t , int32_t); + C4_EXPECT_NARROWER( , int32_t , int64_t); + C4_EXPECT_NARROWER( ! , int32_t , uint8_t ); + C4_EXPECT_NARROWER( ! , int32_t , uint16_t); + C4_EXPECT_NARROWER( , int32_t , uint32_t); + C4_EXPECT_NARROWER( , int32_t , uint64_t); + C4_EXPECT_NARROWER( ! , int64_t , int8_t ); + C4_EXPECT_NARROWER( ! , int64_t , int16_t); + C4_EXPECT_NARROWER( ! , int64_t , int32_t); + C4_EXPECT_NARROWER( ! , int64_t , int64_t); + C4_EXPECT_NARROWER( ! , int64_t , uint8_t ); + C4_EXPECT_NARROWER( ! , int64_t , uint16_t); + C4_EXPECT_NARROWER( ! , int64_t , uint32_t); + C4_EXPECT_NARROWER( , int64_t , uint64_t); +} + +TEST_CASE("is_narrower_size.unsigned_types") +{ + C4_EXPECT_NARROWER( ! , uint8_t , int8_t ); + C4_EXPECT_NARROWER( , uint8_t , int16_t); + C4_EXPECT_NARROWER( , uint8_t , int32_t); + C4_EXPECT_NARROWER( , uint8_t , int64_t); + C4_EXPECT_NARROWER( ! , uint8_t , uint8_t ); + C4_EXPECT_NARROWER( , uint8_t , uint16_t); + C4_EXPECT_NARROWER( , uint8_t , uint32_t); + C4_EXPECT_NARROWER( , uint8_t , uint64_t); + C4_EXPECT_NARROWER( ! , uint16_t , int8_t ); + C4_EXPECT_NARROWER( ! , uint16_t , int16_t); + C4_EXPECT_NARROWER( , uint16_t , int32_t); + C4_EXPECT_NARROWER( , uint16_t , int64_t); + C4_EXPECT_NARROWER( ! , uint16_t , uint8_t ); + C4_EXPECT_NARROWER( ! , uint16_t , uint16_t); + C4_EXPECT_NARROWER( , uint16_t , uint32_t); + C4_EXPECT_NARROWER( , uint16_t , uint64_t); + C4_EXPECT_NARROWER( ! , uint32_t , int8_t ); + C4_EXPECT_NARROWER( ! , uint32_t , int16_t); + C4_EXPECT_NARROWER( ! , uint32_t , int32_t); + C4_EXPECT_NARROWER( , uint32_t , int64_t); + C4_EXPECT_NARROWER( ! , uint32_t , uint8_t ); + C4_EXPECT_NARROWER( ! , uint32_t , uint16_t); + C4_EXPECT_NARROWER( ! , uint32_t , uint32_t); + C4_EXPECT_NARROWER( , uint32_t , uint64_t); + C4_EXPECT_NARROWER( ! , uint64_t , int8_t ); + C4_EXPECT_NARROWER( ! , uint64_t , int16_t); + C4_EXPECT_NARROWER( ! , uint64_t , int32_t); + C4_EXPECT_NARROWER( ! , uint64_t , int64_t); + C4_EXPECT_NARROWER( ! , uint64_t , uint8_t ); + C4_EXPECT_NARROWER( ! , uint64_t , uint16_t); + C4_EXPECT_NARROWER( ! , uint64_t , uint32_t); + C4_EXPECT_NARROWER( ! , uint64_t , uint64_t); +} + +template<typename I, typename O> +typename std::enable_if<std::is_same<I, O>::value, void>::type +test_szconv() +{ + // nothing to do here +} + +template<typename I, typename O> +typename std::enable_if< ! std::is_same<I, O>::value, void>::type +test_szconv() +{ + C4_STATIC_ASSERT(std::is_integral<I>::value); + C4_STATIC_ASSERT(std::is_integral<O>::value); + + const I imax = std::numeric_limits<I>::max(); + const I imin = std::numeric_limits<I>::min(); + const O omax = std::numeric_limits<O>::max(); + const O omin = std::numeric_limits<O>::min(); + + CHECK_EQ(szconv<O>(I(0)), O(0)); + CHECK_EQ(szconv<I>(I(0)), I(0)); + +#if C4_USE_XASSERT + if((uint64_t)omax < (uint64_t)imax) + { + C4_EXPECT_ERROR_OCCURS(); + O out = szconv<O>(imax); + } + else if((uint64_t)omax > (uint64_t)imax) + { + C4_EXPECT_ERROR_OCCURS(); + I out = szconv<I>(omax); + } +#endif +} + +#define DO_TEST_SZCONV(ty) \ + TEST_CASE("szconv." #ty "_to_int8") \ + { \ + test_szconv<ty##_t, int8_t>(); \ + } \ + TEST_CASE("zconv." #ty "_to_uint8") \ + { \ + test_szconv<ty##_t, uint8_t>(); \ + } \ + TEST_CASE("zconv." #ty "_to_int16") \ + { \ + test_szconv<ty##_t, int16_t>(); \ + } \ + TEST_CASE("zconv." #ty "_to_uint16") \ + { \ + test_szconv<ty##_t, uint16_t>(); \ + } \ + TEST_CASE("zconv." #ty "_to_int32") \ + { \ + test_szconv<ty##_t, int32_t>(); \ + } \ + TEST_CASE("zconv." #ty "_to_uint32") \ + { \ + test_szconv<ty##_t, uint32_t>(); \ + } \ + TEST_CASE("zconv." #ty "_to_int64") \ + { \ + test_szconv<ty##_t, int64_t>(); \ + } \ + TEST_CASE("szconv." #ty "_to_uint64") \ + { \ + test_szconv<ty##_t, uint64_t>(); \ + } + +DO_TEST_SZCONV(int8) +DO_TEST_SZCONV(uint8) +DO_TEST_SZCONV(int16) +DO_TEST_SZCONV(uint16) +DO_TEST_SZCONV(int32) +DO_TEST_SZCONV(uint32) +DO_TEST_SZCONV(int64) +DO_TEST_SZCONV(uint64) + +} // namespace c4 + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_type_name.cpp b/thirdparty/ryml/ext/c4core/test/test_type_name.cpp new file mode 100644 index 000000000..422676bcf --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_type_name.cpp @@ -0,0 +1,49 @@ +#include "c4/test.hpp" + +#ifndef C4CORE_SINGLE_HEADER +#include "c4/type_name.hpp" +#endif + +class SomeTypeName {}; +struct SomeStructName {}; + +namespace c4 { + +class SomeTypeNameInsideANamespace {}; +struct SomeStructNameInsideANamespace {}; + +template<size_t N> +cspan<char> cstr(const char (&s)[N]) +{ + cspan<char> o(s, N-1); + return o; +} + +TEST_CASE("type_name.intrinsic_types") +{ + CHECK_EQ(type_name<int>(), cstr("int")); + CHECK_EQ(type_name<float>(), cstr("float")); + CHECK_EQ(type_name<double>(), cstr("double")); +} +TEST_CASE("type_name.classes") +{ + CHECK_EQ(type_name<SomeTypeName>(), cstr("SomeTypeName")); + CHECK_EQ(type_name<::SomeTypeName>(), cstr("SomeTypeName")); +} +TEST_CASE("type_name.structs") +{ + CHECK_EQ(type_name<SomeStructName>(), cstr("SomeStructName")); + CHECK_EQ(type_name<::SomeStructName>(), cstr("SomeStructName")); +} +TEST_CASE("type_name.inside_namespace") +{ + CHECK_EQ(type_name<SomeTypeNameInsideANamespace>(), cstr("c4::SomeTypeNameInsideANamespace")); + CHECK_EQ(type_name<c4::SomeTypeNameInsideANamespace>(), cstr("c4::SomeTypeNameInsideANamespace")); + CHECK_EQ(type_name<::c4::SomeTypeNameInsideANamespace>(), cstr("c4::SomeTypeNameInsideANamespace")); + + CHECK_EQ(type_name<SomeStructNameInsideANamespace>(), cstr("c4::SomeStructNameInsideANamespace")); + CHECK_EQ(type_name<c4::SomeStructNameInsideANamespace>(), cstr("c4::SomeStructNameInsideANamespace")); + CHECK_EQ(type_name<::c4::SomeStructNameInsideANamespace>(), cstr("c4::SomeStructNameInsideANamespace")); +} + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/test/test_types.cpp b/thirdparty/ryml/ext/c4core/test/test_types.cpp new file mode 100644 index 000000000..697ce289d --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_types.cpp @@ -0,0 +1,81 @@ +#ifndef C4CORE_SINGLE_HEADER +#include "c4/config.hpp" +#endif +#include <string> + +#include "c4/libtest/supprwarn_push.hpp" +#include "c4/test.hpp" + +namespace c4 { + +C4_STATIC_ASSERT((std::is_same< fastcref< char >, char >::value)); +C4_STATIC_ASSERT((std::is_same< fastcref< i8 >, i8 >::value)); +C4_STATIC_ASSERT((std::is_same< fastcref< u8 >, u8 >::value)); +C4_STATIC_ASSERT((std::is_same< fastcref< i16 >, i16 >::value)); +C4_STATIC_ASSERT((std::is_same< fastcref< u16 >, u16 >::value)); +C4_STATIC_ASSERT((std::is_same< fastcref< i32 >, i32 >::value)); +C4_STATIC_ASSERT((std::is_same< fastcref< u32 >, u32 >::value)); +C4_STATIC_ASSERT((std::is_same< fastcref< i64 >, i64 >::value)); +C4_STATIC_ASSERT((std::is_same< fastcref< u64 >, u64 >::value)); + +using carr64 = char[64]; +C4_STATIC_ASSERT((std::is_same< fastcref< carr64 >, carr64 const& >::value)); +C4_STATIC_ASSERT((std::is_same< fastcref< std::string >, std::string const& >::value)); + +//----------------------------------------------------------------------------- + +C4_BEGIN_HIDDEN_NAMESPACE +template< class T > struct ufonix { T a; }; +using F = ufonix< uint32_t >; +C4_END_HIDDEN_NAMESPACE + +TEST_CASE("TestSizeStructs.min_remainder") +{ + CHECK_EQ(min_remainder(4, 6), 2); + CHECK_EQ(min_remainder(6, 6), 0); + CHECK_EQ(min_remainder(8, 6), 0); +} + +TEST_CASE("TestSizeStructs.mult_remainder") +{ + CHECK_EQ(mult_remainder(6, 1), 0); + CHECK_EQ(mult_remainder(6, 2), 0); + CHECK_EQ(mult_remainder(6, 3), 0); + CHECK_EQ(mult_remainder(6, 4), 2); + CHECK_EQ(mult_remainder(6, 5), 4); + CHECK_EQ(mult_remainder(6, 6), 0); + CHECK_EQ(mult_remainder(6, 7), 1); +} +TEST_CASE("TestSizeStructs.Padded") +{ + CHECK_EQ(sizeof(F), sizeof(uint32_t)); + CHECK_EQ((sizeof(Padded< F, 0 >)), sizeof(F)); + CHECK_EQ((sizeof(Padded< F, 1 >)), sizeof(F)+1); + CHECK_EQ((sizeof(Padded< F, 2 >)), sizeof(F)+2); + CHECK_EQ((sizeof(Padded< F, 3 >)), sizeof(F)+3); +} +TEST_CASE("TestSizeStructs.MinSized") +{ + CHECK_EQ((sizeof(MinSized< F, 14 >)), 14); + CHECK_EQ((sizeof(MinSized< F, 15 >)), 15); + CHECK_EQ((sizeof(MinSized< F, 16 >)), 16); + CHECK_EQ((sizeof(MinSized< F, 17 >)), 17); +} +TEST_CASE("TestSizeStructs.MultSized") +{ + using G = ufonix< char[8] >; + CHECK_EQ((sizeof(MultSized< G, 7 >)), 14); + CHECK_EQ((sizeof(MultSized< G, 6 >)), 12); + CHECK_EQ((sizeof(MultSized< G, 5 >)), 10); + CHECK_EQ((sizeof(MultSized< G, 4 >)), 8); +} +TEST_CASE("TestSizeStructs.UbufSized") +{ + CHECK_EQ((sizeof(UbufSized<ufonix<char[63]>>)), 64); + CHECK_EQ((sizeof(UbufSized<ufonix<char[64]>>)), 64); + CHECK_EQ((sizeof(UbufSized<ufonix<char[65]>>)), 80); +} + +} // namespace c4 + +#include "c4/libtest/supprwarn_pop.hpp" diff --git a/thirdparty/ryml/ext/c4core/test/test_utf.cpp b/thirdparty/ryml/ext/c4core/test/test_utf.cpp new file mode 100644 index 000000000..ffa4fb338 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/test_utf.cpp @@ -0,0 +1,48 @@ +#include "c4/test.hpp" +#ifndef C4CORE_SINGLE_HEADER +#include "c4/std/string.hpp" +#include "c4/std/vector.hpp" +#include "c4/format.hpp" +#include "c4/utf.hpp" +#endif + +#include "c4/libtest/supprwarn_push.hpp" + +#include <cstring> + +namespace c4 { + +struct utft +{ + csubstr code_point; + csubstr character; + uint32_t character_val; + csubstr character_val_hex; +}; +constexpr const utft utf_chars[] = { +#include "./utfchars.inc" +}; + +TEST_CASE("utf.decode_code_point") +{ + size_t i = 0; + char decoded_buf[64]; + for(auto uc : utf_chars) + { + INFO("utfchars[", i, "]: codepoint=", uc.code_point, ' ', + "character=", uc.character.empty() ? csubstr{} : uc.character, ' ', + "val=", uc.character_val_hex, '(', uc.character_val, ')'); + i++; + csubstr cpstr = uc.code_point.sub(2).triml('0'); + if(cpstr.empty()) + continue; + csubstr decoded = decode_code_point(decoded_buf, cpstr); + CHECK_UNARY(uc.code_point.begins_with("U+")); + if(uc.character.empty()) + continue; + CHECK_EQ(decoded.len, uc.character.len); + CHECK_EQ(decoded, uc.character); + } +} + +} // namespace c4 diff --git a/thirdparty/ryml/ext/c4core/test/utfchars.inc b/thirdparty/ryml/ext/c4core/test/utfchars.inc new file mode 100644 index 000000000..b0b23d4c0 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/test/utfchars.inc @@ -0,0 +1,6274 @@ +// https://www.utf8-chartable.de/unicode-utf8-table.pl +#define _c(cp, wysiwyg, hex) utft{csubstr{#cp}, csubstr{wysiwyg}, UINT32_C(0x##hex), csubstr{"0x" #hex}} +_c(U+0000, "\0", 00), +_c(U+0001, "", 01), +_c(U+0002, "", 02), +_c(U+0003, "", 03), +_c(U+0004, "", 04), +_c(U+0005, "", 05), +_c(U+0006, "", 06), +_c(U+0007, "", 07), +_c(U+0008, "\b", 08), +_c(U+0009, "\t", 09), +_c(U+000A, "\n", 0a), +_c(U+000B, "", 0b), +_c(U+000C, "", 0c), +_c(U+000D, "\r", 0d), +_c(U+000E, "", 0e), +_c(U+000F, "", 0f), +_c(U+0010, "", 10), +_c(U+0011, "", 11), +_c(U+0012, "", 12), +_c(U+0013, "", 13), +_c(U+0014, "", 14), +_c(U+0015, "", 15), +_c(U+0016, "", 16), +_c(U+0017, "", 17), +_c(U+0018, "", 18), +_c(U+0019, "", 19), +_c(U+001A, "", 1a), +_c(U+001B, "", 1b), +_c(U+001C, "", 1c), +_c(U+001D, "", 1d), +_c(U+001E, "", 1e), +_c(U+001F, "", 1f), +_c(U+0020, " ", 20), +_c(U+0021, "!", 21), +_c(U+0022, "\"", 22), +_c(U+0023, "#", 23), +_c(U+0024, "$", 24), +_c(U+0025, "%", 25), +_c(U+0026, "&", 26), +_c(U+0027, "'", 27), +_c(U+0028, "(", 28), +_c(U+0029, ")", 29), +_c(U+002A, "*", 2a), +_c(U+002B, "+", 2b), +_c(U+002C, ",", 2c), +_c(U+002D, "-", 2d), +_c(U+002E, ".", 2e), +_c(U+002F, "/", 2f), +_c(U+0030, "0", 30), +_c(U+0031, "1", 31), +_c(U+0032, "2", 32), +_c(U+0033, "3", 33), +_c(U+0034, "4", 34), +_c(U+0035, "5", 35), +_c(U+0036, "6", 36), +_c(U+0037, "7", 37), +_c(U+0038, "8", 38), +_c(U+0039, "9", 39), +_c(U+003A, ":", 3a), +_c(U+003B, ";", 3b), +_c(U+003C, "<", 3c), +_c(U+003D, "=", 3d), +_c(U+003E, ">", 3e), +_c(U+003F, "?", 3f), +_c(U+0040, "@", 40), +_c(U+0041, "A", 41), +_c(U+0042, "B", 42), +_c(U+0043, "C", 43), +_c(U+0044, "D", 44), +_c(U+0045, "E", 45), +_c(U+0046, "F", 46), +_c(U+0047, "G", 47), +_c(U+0048, "H", 48), +_c(U+0049, "I", 49), +_c(U+004A, "J", 4a), +_c(U+004B, "K", 4b), +_c(U+004C, "L", 4c), +_c(U+004D, "M", 4d), +_c(U+004E, "N", 4e), +_c(U+004F, "O", 4f), +_c(U+0050, "P", 50), +_c(U+0051, "Q", 51), +_c(U+0052, "R", 52), +_c(U+0053, "S", 53), +_c(U+0054, "T", 54), +_c(U+0055, "U", 55), +_c(U+0056, "V", 56), +_c(U+0057, "W", 57), +_c(U+0058, "X", 58), +_c(U+0059, "Y", 59), +_c(U+005A, "Z", 5a), +_c(U+005B, "[", 5b), +_c(U+005C, "\\", 5c), +_c(U+005D, "]", 5d), +_c(U+005E, "^", 5e), +_c(U+005F, "_", 5f), +_c(U+0060, "`", 60), +_c(U+0061, "a", 61), +_c(U+0062, "b", 62), +_c(U+0063, "c", 63), +_c(U+0064, "d", 64), +_c(U+0065, "e", 65), +_c(U+0066, "f", 66), +_c(U+0067, "g", 67), +_c(U+0068, "h", 68), +_c(U+0069, "i", 69), +_c(U+006A, "j", 6a), +_c(U+006B, "k", 6b), +_c(U+006C, "l", 6c), +_c(U+006D, "m", 6d), +_c(U+006E, "n", 6e), +_c(U+006F, "o", 6f), +_c(U+0070, "p", 70), +_c(U+0071, "q", 71), +_c(U+0072, "r", 72), +_c(U+0073, "s", 73), +_c(U+0074, "t", 74), +_c(U+0075, "u", 75), +_c(U+0076, "v", 76), +_c(U+0077, "w", 77), +_c(U+0078, "x", 78), +_c(U+0079, "y", 79), +_c(U+007A, "z", 7a), +_c(U+007B, "", 7b), +_c(U+007C, "|", 7c), +_c(U+007D, "}", 7d), +_c(U+007E, "~", 7e), +_c(U+007F, "", 7f), // del +_c(U+0080, "", c280), +_c(U+0081, "", c281), +_c(U+0082, "", c282), +_c(U+0083, "", c283), +_c(U+0084, "", c284), +_c(U+0085, "", c285), +_c(U+0086, "", c286), +_c(U+0087, "", c287), +_c(U+0088, "", c288), +_c(U+0089, "", c289), +_c(U+008A, "", c28a), +_c(U+008B, "", c28b), +_c(U+008C, "", c28c), +_c(U+008D, "", c28d), +_c(U+008E, "", c28e), +_c(U+008F, "", c28f), +_c(U+0090, "", c290), +_c(U+0091, "", c291), +_c(U+0092, "", c292), +_c(U+0093, "", c293), +_c(U+0094, "", c294), +_c(U+0095, "", c295), +_c(U+0096, "", c296), +_c(U+0097, "", c297), +_c(U+0098, "", c298), +_c(U+0099, "", c299), +_c(U+009A, "", c29a), +_c(U+009B, "", c29b), +_c(U+009C, "", c29c), +_c(U+009D, "", c29d), +_c(U+009E, "", c29e), +_c(U+009F, "", c29f), +_c(U+00A0, "", c2a0), +_c(U+00A1, "¡", c2a1), +_c(U+00A2, "¢", c2a2), +_c(U+00A3, "£", c2a3), +_c(U+00A4, "¤", c2a4), +_c(U+00A5, "¥", c2a5), +_c(U+00A6, "¦", c2a6), +_c(U+00A7, "§", c2a7), +_c(U+00A8, "¨", c2a8), +_c(U+00A9, "©", c2a9), +_c(U+00AA, "ª", c2aa), +_c(U+00AB, "«", c2ab), +_c(U+00AC, "¬", c2ac), +_c(U+00AD, "", c2ad), +_c(U+00AE, "®", c2ae), +_c(U+00AF, "¯", c2af), +_c(U+00B0, "°", c2b0), +_c(U+00B1, "±", c2b1), +_c(U+00B2, "²", c2b2), +_c(U+00B3, "³", c2b3), +_c(U+00B4, "´", c2b4), +_c(U+00B5, "µ", c2b5), +_c(U+00B6, "¶", c2b6), +_c(U+00B7, "·", c2b7), +_c(U+00B8, "¸", c2b8), +_c(U+00B9, "¹", c2b9), +_c(U+00BA, "º", c2ba), +_c(U+00BB, "»", c2bb), +_c(U+00BC, "¼", c2bc), +_c(U+00BD, "½", c2bd), +_c(U+00BE, "¾", c2be), +_c(U+00BF, "¿", c2bf), +_c(U+00C0, "À", c380), +_c(U+00C1, "Á", c381), +_c(U+00C2, "Â", c382), +_c(U+00C3, "Ã", c383), +_c(U+00C4, "Ä", c384), +_c(U+00C5, "Å", c385), +_c(U+00C6, "Æ", c386), +_c(U+00C7, "Ç", c387), +_c(U+00C8, "È", c388), +_c(U+00C9, "É", c389), +_c(U+00CA, "Ê", c38a), +_c(U+00CB, "Ë", c38b), +_c(U+00CC, "Ì", c38c), +_c(U+00CD, "Í", c38d), +_c(U+00CE, "Î", c38e), +_c(U+00CF, "Ï", c38f), +_c(U+00D0, "Ð", c390), +_c(U+00D1, "Ñ", c391), +_c(U+00D2, "Ò", c392), +_c(U+00D3, "Ó", c393), +_c(U+00D4, "Ô", c394), +_c(U+00D5, "Õ", c395), +_c(U+00D6, "Ö", c396), +_c(U+00D7, "×", c397), +_c(U+00D8, "Ø", c398), +_c(U+00D9, "Ù", c399), +_c(U+00DA, "Ú", c39a), +_c(U+00DB, "Û", c39b), +_c(U+00DC, "Ü", c39c), +_c(U+00DD, "Ý", c39d), +_c(U+00DE, "Þ", c39e), +_c(U+00DF, "ß", c39f), +_c(U+00E0, "à", c3a0), +_c(U+00E1, "á", c3a1), +_c(U+00E2, "â", c3a2), +_c(U+00E3, "ã", c3a3), +_c(U+00E4, "ä", c3a4), +_c(U+00E5, "å", c3a5), +_c(U+00E6, "æ", c3a6), +_c(U+00E7, "ç", c3a7), +_c(U+00E8, "è", c3a8), +_c(U+00E9, "é", c3a9), +_c(U+00EA, "ê", c3aa), +_c(U+00EB, "ë", c3ab), +_c(U+00EC, "ì", c3ac), +_c(U+00ED, "í", c3ad), +_c(U+00EE, "î", c3ae), +_c(U+00EF, "ï", c3af), +_c(U+00F0, "ð", c3b0), +_c(U+00F1, "ñ", c3b1), +_c(U+00F2, "ò", c3b2), +_c(U+00F3, "ó", c3b3), +_c(U+00F4, "ô", c3b4), +_c(U+00F5, "õ", c3b5), +_c(U+00F6, "ö", c3b6), +_c(U+00F7, "÷", c3b7), +_c(U+00F8, "ø", c3b8), +_c(U+00F9, "ù", c3b9), +_c(U+00FA, "ú", c3ba), +_c(U+00FB, "û", c3bb), +_c(U+00FC, "ü", c3bc), +_c(U+00FD, "ý", c3bd), +_c(U+00FE, "þ", c3be), +_c(U+00FF, "ÿ", c3bf), +_c(U+0100, "Ā", c480), +_c(U+0101, "ā", c481), +_c(U+0102, "Ă", c482), +_c(U+0103, "ă", c483), +_c(U+0104, "Ą", c484), +_c(U+0105, "ą", c485), +_c(U+0106, "Ć", c486), +_c(U+0107, "ć", c487), +_c(U+0108, "Ĉ", c488), +_c(U+0109, "ĉ", c489), +_c(U+010A, "Ċ", c48a), +_c(U+010B, "ċ", c48b), +_c(U+010C, "Č", c48c), +_c(U+010D, "č", c48d), +_c(U+010E, "Ď", c48e), +_c(U+010F, "ď", c48f), +_c(U+0110, "Đ", c490), +_c(U+0111, "đ", c491), +_c(U+0112, "Ē", c492), +_c(U+0113, "ē", c493), +_c(U+0114, "Ĕ", c494), +_c(U+0115, "ĕ", c495), +_c(U+0116, "Ė", c496), +_c(U+0117, "ė", c497), +_c(U+0118, "Ę", c498), +_c(U+0119, "ę", c499), +_c(U+011A, "Ě", c49a), +_c(U+011B, "ě", c49b), +_c(U+011C, "Ĝ", c49c), +_c(U+011D, "ĝ", c49d), +_c(U+011E, "Ğ", c49e), +_c(U+011F, "ğ", c49f), +_c(U+0120, "Ġ", c4a0), +_c(U+0121, "ġ", c4a1), +_c(U+0122, "Ģ", c4a2), +_c(U+0123, "ģ", c4a3), +_c(U+0124, "Ĥ", c4a4), +_c(U+0125, "ĥ", c4a5), +_c(U+0126, "Ħ", c4a6), +_c(U+0127, "ħ", c4a7), +_c(U+0128, "Ĩ", c4a8), +_c(U+0129, "ĩ", c4a9), +_c(U+012A, "Ī", c4aa), +_c(U+012B, "ī", c4ab), +_c(U+012C, "Ĭ", c4ac), +_c(U+012D, "ĭ", c4ad), +_c(U+012E, "Į", c4ae), +_c(U+012F, "į", c4af), +_c(U+0130, "İ", c4b0), +_c(U+0131, "ı", c4b1), +_c(U+0132, "IJ", c4b2), +_c(U+0133, "ij", c4b3), +_c(U+0134, "Ĵ", c4b4), +_c(U+0135, "ĵ", c4b5), +_c(U+0136, "Ķ", c4b6), +_c(U+0137, "ķ", c4b7), +_c(U+0138, "ĸ", c4b8), +_c(U+0139, "Ĺ", c4b9), +_c(U+013A, "ĺ", c4ba), +_c(U+013B, "Ļ", c4bb), +_c(U+013C, "ļ", c4bc), +_c(U+013D, "Ľ", c4bd), +_c(U+013E, "ľ", c4be), +_c(U+013F, "Ŀ", c4bf), +_c(U+0140, "ŀ", c580), +_c(U+0141, "Ł", c581), +_c(U+0142, "ł", c582), +_c(U+0143, "Ń", c583), +_c(U+0144, "ń", c584), +_c(U+0145, "Ņ", c585), +_c(U+0146, "ņ", c586), +_c(U+0147, "Ň", c587), +_c(U+0148, "ň", c588), +_c(U+0149, "ʼn", c589), +_c(U+014A, "Ŋ", c58a), +_c(U+014B, "ŋ", c58b), +_c(U+014C, "Ō", c58c), +_c(U+014D, "ō", c58d), +_c(U+014E, "Ŏ", c58e), +_c(U+014F, "ŏ", c58f), +_c(U+0150, "Ő", c590), +_c(U+0151, "ő", c591), +_c(U+0152, "Œ", c592), +_c(U+0153, "œ", c593), +_c(U+0154, "Ŕ", c594), +_c(U+0155, "ŕ", c595), +_c(U+0156, "Ŗ", c596), +_c(U+0157, "ŗ", c597), +_c(U+0158, "Ř", c598), +_c(U+0159, "ř", c599), +_c(U+015A, "Ś", c59a), +_c(U+015B, "ś", c59b), +_c(U+015C, "Ŝ", c59c), +_c(U+015D, "ŝ", c59d), +_c(U+015E, "Ş", c59e), +_c(U+015F, "ş", c59f), +_c(U+0160, "Š", c5a0), +_c(U+0161, "š", c5a1), +_c(U+0162, "Ţ", c5a2), +_c(U+0163, "ţ", c5a3), +_c(U+0164, "Ť", c5a4), +_c(U+0165, "ť", c5a5), +_c(U+0166, "Ŧ", c5a6), +_c(U+0167, "ŧ", c5a7), +_c(U+0168, "Ũ", c5a8), +_c(U+0169, "ũ", c5a9), +_c(U+016A, "Ū", c5aa), +_c(U+016B, "ū", c5ab), +_c(U+016C, "Ŭ", c5ac), +_c(U+016D, "ŭ", c5ad), +_c(U+016E, "Ů", c5ae), +_c(U+016F, "ů", c5af), +_c(U+0170, "Ű", c5b0), +_c(U+0171, "ű", c5b1), +_c(U+0172, "Ų", c5b2), +_c(U+0173, "ų", c5b3), +_c(U+0174, "Ŵ", c5b4), +_c(U+0175, "ŵ", c5b5), +_c(U+0176, "Ŷ", c5b6), +_c(U+0177, "ŷ", c5b7), +_c(U+0178, "Ÿ", c5b8), +_c(U+0179, "Ź", c5b9), +_c(U+017A, "ź", c5ba), +_c(U+017B, "Ż", c5bb), +_c(U+017C, "ż", c5bc), +_c(U+017D, "Ž", c5bd), +_c(U+017E, "ž", c5be), +_c(U+017F, "ſ", c5bf), +_c(U+0180, "ƀ", c680), +_c(U+0181, "Ɓ", c681), +_c(U+0182, "Ƃ", c682), +_c(U+0183, "ƃ", c683), +_c(U+0184, "Ƅ", c684), +_c(U+0185, "ƅ", c685), +_c(U+0186, "Ɔ", c686), +_c(U+0187, "Ƈ", c687), +_c(U+0188, "ƈ", c688), +_c(U+0189, "Ɖ", c689), +_c(U+018A, "Ɗ", c68a), +_c(U+018B, "Ƌ", c68b), +_c(U+018C, "ƌ", c68c), +_c(U+018D, "ƍ", c68d), +_c(U+018E, "Ǝ", c68e), +_c(U+018F, "Ə", c68f), +_c(U+0190, "Ɛ", c690), +_c(U+0191, "Ƒ", c691), +_c(U+0192, "ƒ", c692), +_c(U+0193, "Ɠ", c693), +_c(U+0194, "Ɣ", c694), +_c(U+0195, "ƕ", c695), +_c(U+0196, "Ɩ", c696), +_c(U+0197, "Ɨ", c697), +_c(U+0198, "Ƙ", c698), +_c(U+0199, "ƙ", c699), +_c(U+019A, "ƚ", c69a), +_c(U+019B, "ƛ", c69b), +_c(U+019C, "Ɯ", c69c), +_c(U+019D, "Ɲ", c69d), +_c(U+019E, "ƞ", c69e), +_c(U+019F, "Ɵ", c69f), +_c(U+01A0, "Ơ", c6a0), +_c(U+01A1, "ơ", c6a1), +_c(U+01A2, "Ƣ", c6a2), +_c(U+01A3, "ƣ", c6a3), +_c(U+01A4, "Ƥ", c6a4), +_c(U+01A5, "ƥ", c6a5), +_c(U+01A6, "Ʀ", c6a6), +_c(U+01A7, "Ƨ", c6a7), +_c(U+01A8, "ƨ", c6a8), +_c(U+01A9, "Ʃ", c6a9), +_c(U+01AA, "ƪ", c6aa), +_c(U+01AB, "ƫ", c6ab), +_c(U+01AC, "Ƭ", c6ac), +_c(U+01AD, "ƭ", c6ad), +_c(U+01AE, "Ʈ", c6ae), +_c(U+01AF, "Ư", c6af), +_c(U+01B0, "ư", c6b0), +_c(U+01B1, "Ʊ", c6b1), +_c(U+01B2, "Ʋ", c6b2), +_c(U+01B3, "Ƴ", c6b3), +_c(U+01B4, "ƴ", c6b4), +_c(U+01B5, "Ƶ", c6b5), +_c(U+01B6, "ƶ", c6b6), +_c(U+01B7, "Ʒ", c6b7), +_c(U+01B8, "Ƹ", c6b8), +_c(U+01B9, "ƹ", c6b9), +_c(U+01BA, "ƺ", c6ba), +_c(U+01BB, "ƻ", c6bb), +_c(U+01BC, "Ƽ", c6bc), +_c(U+01BD, "ƽ", c6bd), +_c(U+01BE, "ƾ", c6be), +_c(U+01BF, "ƿ", c6bf), +_c(U+01C0, "ǀ", c780), +_c(U+01C1, "ǁ", c781), +_c(U+01C2, "ǂ", c782), +_c(U+01C3, "ǃ", c783), +_c(U+01C4, "DŽ", c784), +_c(U+01C5, "Dž", c785), +_c(U+01C6, "dž", c786), +_c(U+01C7, "LJ", c787), +_c(U+01C8, "Lj", c788), +_c(U+01C9, "lj", c789), +_c(U+01CA, "NJ", c78a), +_c(U+01CB, "Nj", c78b), +_c(U+01CC, "nj", c78c), +_c(U+01CD, "Ǎ", c78d), +_c(U+01CE, "ǎ", c78e), +_c(U+01CF, "Ǐ", c78f), +_c(U+01D0, "ǐ", c790), +_c(U+01D1, "Ǒ", c791), +_c(U+01D2, "ǒ", c792), +_c(U+01D3, "Ǔ", c793), +_c(U+01D4, "ǔ", c794), +_c(U+01D5, "Ǖ", c795), +_c(U+01D6, "ǖ", c796), +_c(U+01D7, "Ǘ", c797), +_c(U+01D8, "ǘ", c798), +_c(U+01D9, "Ǚ", c799), +_c(U+01DA, "ǚ", c79a), +_c(U+01DB, "Ǜ", c79b), +_c(U+01DC, "ǜ", c79c), +_c(U+01DD, "ǝ", c79d), +_c(U+01DE, "Ǟ", c79e), +_c(U+01DF, "ǟ", c79f), +_c(U+01E0, "Ǡ", c7a0), +_c(U+01E1, "ǡ", c7a1), +_c(U+01E2, "Ǣ", c7a2), +_c(U+01E3, "ǣ", c7a3), +_c(U+01E4, "Ǥ", c7a4), +_c(U+01E5, "ǥ", c7a5), +_c(U+01E6, "Ǧ", c7a6), +_c(U+01E7, "ǧ", c7a7), +_c(U+01E8, "Ǩ", c7a8), +_c(U+01E9, "ǩ", c7a9), +_c(U+01EA, "Ǫ", c7aa), +_c(U+01EB, "ǫ", c7ab), +_c(U+01EC, "Ǭ", c7ac), +_c(U+01ED, "ǭ", c7ad), +_c(U+01EE, "Ǯ", c7ae), +_c(U+01EF, "ǯ", c7af), +_c(U+01F0, "ǰ", c7b0), +_c(U+01F1, "DZ", c7b1), +_c(U+01F2, "Dz", c7b2), +_c(U+01F3, "dz", c7b3), +_c(U+01F4, "Ǵ", c7b4), +_c(U+01F5, "ǵ", c7b5), +_c(U+01F6, "Ƕ", c7b6), +_c(U+01F7, "Ƿ", c7b7), +_c(U+01F8, "Ǹ", c7b8), +_c(U+01F9, "ǹ", c7b9), +_c(U+01FA, "Ǻ", c7ba), +_c(U+01FB, "ǻ", c7bb), +_c(U+01FC, "Ǽ", c7bc), +_c(U+01FD, "ǽ", c7bd), +_c(U+01FE, "Ǿ", c7be), +_c(U+01FF, "ǿ", c7bf), +_c(U+0200, "Ȁ", c880), +_c(U+0201, "ȁ", c881), +_c(U+0202, "Ȃ", c882), +_c(U+0203, "ȃ", c883), +_c(U+0204, "Ȅ", c884), +_c(U+0205, "ȅ", c885), +_c(U+0206, "Ȇ", c886), +_c(U+0207, "ȇ", c887), +_c(U+0208, "Ȉ", c888), +_c(U+0209, "ȉ", c889), +_c(U+020A, "Ȋ", c88a), +_c(U+020B, "ȋ", c88b), +_c(U+020C, "Ȍ", c88c), +_c(U+020D, "ȍ", c88d), +_c(U+020E, "Ȏ", c88e), +_c(U+020F, "ȏ", c88f), +_c(U+0210, "Ȑ", c890), +_c(U+0211, "ȑ", c891), +_c(U+0212, "Ȓ", c892), +_c(U+0213, "ȓ", c893), +_c(U+0214, "Ȕ", c894), +_c(U+0215, "ȕ", c895), +_c(U+0216, "Ȗ", c896), +_c(U+0217, "ȗ", c897), +_c(U+0218, "Ș", c898), +_c(U+0219, "ș", c899), +_c(U+021A, "Ț", c89a), +_c(U+021B, "ț", c89b), +_c(U+021C, "Ȝ", c89c), +_c(U+021D, "ȝ", c89d), +_c(U+021E, "Ȟ", c89e), +_c(U+021F, "ȟ", c89f), +_c(U+0220, "Ƞ", c8a0), +_c(U+0221, "ȡ", c8a1), +_c(U+0222, "Ȣ", c8a2), +_c(U+0223, "ȣ", c8a3), +_c(U+0224, "Ȥ", c8a4), +_c(U+0225, "ȥ", c8a5), +_c(U+0226, "Ȧ", c8a6), +_c(U+0227, "ȧ", c8a7), +_c(U+0228, "Ȩ", c8a8), +_c(U+0229, "ȩ", c8a9), +_c(U+022A, "Ȫ", c8aa), +_c(U+022B, "ȫ", c8ab), +_c(U+022C, "Ȭ", c8ac), +_c(U+022D, "ȭ", c8ad), +_c(U+022E, "Ȯ", c8ae), +_c(U+022F, "ȯ", c8af), +_c(U+0230, "Ȱ", c8b0), +_c(U+0231, "ȱ", c8b1), +_c(U+0232, "Ȳ", c8b2), +_c(U+0233, "ȳ", c8b3), +_c(U+0234, "ȴ", c8b4), +_c(U+0235, "ȵ", c8b5), +_c(U+0236, "ȶ", c8b6), +_c(U+0237, "ȷ", c8b7), +_c(U+0238, "ȸ", c8b8), +_c(U+0239, "ȹ", c8b9), +_c(U+023A, "Ⱥ", c8ba), +_c(U+023B, "Ȼ", c8bb), +_c(U+023C, "ȼ", c8bc), +_c(U+023D, "Ƚ", c8bd), +_c(U+023E, "Ⱦ", c8be), +_c(U+023F, "ȿ", c8bf), +_c(U+0240, "ɀ", c980), +_c(U+0241, "Ɂ", c981), +_c(U+0242, "ɂ", c982), +_c(U+0243, "Ƀ", c983), +_c(U+0244, "Ʉ", c984), +_c(U+0245, "Ʌ", c985), +_c(U+0246, "Ɇ", c986), +_c(U+0247, "ɇ", c987), +_c(U+0248, "Ɉ", c988), +_c(U+0249, "ɉ", c989), +_c(U+024A, "Ɋ", c98a), +_c(U+024B, "ɋ", c98b), +_c(U+024C, "Ɍ", c98c), +_c(U+024D, "ɍ", c98d), +_c(U+024E, "Ɏ", c98e), +_c(U+024F, "ɏ", c98f), +_c(U+0250, "ɐ", c990), +_c(U+0251, "ɑ", c991), +_c(U+0252, "ɒ", c992), +_c(U+0253, "ɓ", c993), +_c(U+0254, "ɔ", c994), +_c(U+0255, "ɕ", c995), +_c(U+0256, "ɖ", c996), +_c(U+0257, "ɗ", c997), +_c(U+0258, "ɘ", c998), +_c(U+0259, "ə", c999), +_c(U+025A, "ɚ", c99a), +_c(U+025B, "ɛ", c99b), +_c(U+025C, "ɜ", c99c), +_c(U+025D, "ɝ", c99d), +_c(U+025E, "ɞ", c99e), +_c(U+025F, "ɟ", c99f), +_c(U+0260, "ɠ", c9a0), +_c(U+0261, "ɡ", c9a1), +_c(U+0262, "ɢ", c9a2), +_c(U+0263, "ɣ", c9a3), +_c(U+0264, "ɤ", c9a4), +_c(U+0265, "ɥ", c9a5), +_c(U+0266, "ɦ", c9a6), +_c(U+0267, "ɧ", c9a7), +_c(U+0268, "ɨ", c9a8), +_c(U+0269, "ɩ", c9a9), +_c(U+026A, "ɪ", c9aa), +_c(U+026B, "ɫ", c9ab), +_c(U+026C, "ɬ", c9ac), +_c(U+026D, "ɭ", c9ad), +_c(U+026E, "ɮ", c9ae), +_c(U+026F, "ɯ", c9af), +_c(U+0270, "ɰ", c9b0), +_c(U+0271, "ɱ", c9b1), +_c(U+0272, "ɲ", c9b2), +_c(U+0273, "ɳ", c9b3), +_c(U+0274, "ɴ", c9b4), +_c(U+0275, "ɵ", c9b5), +_c(U+0276, "ɶ", c9b6), +_c(U+0277, "ɷ", c9b7), +_c(U+0278, "ɸ", c9b8), +_c(U+0279, "ɹ", c9b9), +_c(U+027A, "ɺ", c9ba), +_c(U+027B, "ɻ", c9bb), +_c(U+027C, "ɼ", c9bc), +_c(U+027D, "ɽ", c9bd), +_c(U+027E, "ɾ", c9be), +_c(U+027F, "ɿ", c9bf), +_c(U+0280, "ʀ", ca80), +_c(U+0281, "ʁ", ca81), +_c(U+0282, "ʂ", ca82), +_c(U+0283, "ʃ", ca83), +_c(U+0284, "ʄ", ca84), +_c(U+0285, "ʅ", ca85), +_c(U+0286, "ʆ", ca86), +_c(U+0287, "ʇ", ca87), +_c(U+0288, "ʈ", ca88), +_c(U+0289, "ʉ", ca89), +_c(U+028A, "ʊ", ca8a), +_c(U+028B, "ʋ", ca8b), +_c(U+028C, "ʌ", ca8c), +_c(U+028D, "ʍ", ca8d), +_c(U+028E, "ʎ", ca8e), +_c(U+028F, "ʏ", ca8f), +_c(U+0290, "ʐ", ca90), +_c(U+0291, "ʑ", ca91), +_c(U+0292, "ʒ", ca92), +_c(U+0293, "ʓ", ca93), +_c(U+0294, "ʔ", ca94), +_c(U+0295, "ʕ", ca95), +_c(U+0296, "ʖ", ca96), +_c(U+0297, "ʗ", ca97), +_c(U+0298, "ʘ", ca98), +_c(U+0299, "ʙ", ca99), +_c(U+029A, "ʚ", ca9a), +_c(U+029B, "ʛ", ca9b), +_c(U+029C, "ʜ", ca9c), +_c(U+029D, "ʝ", ca9d), +_c(U+029E, "ʞ", ca9e), +_c(U+029F, "ʟ", ca9f), +_c(U+02A0, "ʠ", caa0), +_c(U+02A1, "ʡ", caa1), +_c(U+02A2, "ʢ", caa2), +_c(U+02A3, "ʣ", caa3), +_c(U+02A4, "ʤ", caa4), +_c(U+02A5, "ʥ", caa5), +_c(U+02A6, "ʦ", caa6), +_c(U+02A7, "ʧ", caa7), +_c(U+02A8, "ʨ", caa8), +_c(U+02A9, "ʩ", caa9), +_c(U+02AA, "ʪ", caaa), +_c(U+02AB, "ʫ", caab), +_c(U+02AC, "ʬ", caac), +_c(U+02AD, "ʭ", caad), +_c(U+02AE, "ʮ", caae), +_c(U+02AF, "ʯ", caaf), +_c(U+02B0, "ʰ", cab0), +_c(U+02B1, "ʱ", cab1), +_c(U+02B2, "ʲ", cab2), +_c(U+02B3, "ʳ", cab3), +_c(U+02B4, "ʴ", cab4), +_c(U+02B5, "ʵ", cab5), +_c(U+02B6, "ʶ", cab6), +_c(U+02B7, "ʷ", cab7), +_c(U+02B8, "ʸ", cab8), +_c(U+02B9, "ʹ", cab9), +_c(U+02BA, "ʺ", caba), +_c(U+02BB, "ʻ", cabb), +_c(U+02BC, "ʼ", cabc), +_c(U+02BD, "ʽ", cabd), +_c(U+02BE, "ʾ", cabe), +_c(U+02BF, "ʿ", cabf), +_c(U+02C0, "ˀ", cb80), +_c(U+02C1, "ˁ", cb81), +_c(U+02C2, "˂", cb82), +_c(U+02C3, "˃", cb83), +_c(U+02C4, "˄", cb84), +_c(U+02C5, "˅", cb85), +_c(U+02C6, "ˆ", cb86), +_c(U+02C7, "ˇ", cb87), +_c(U+02C8, "ˈ", cb88), +_c(U+02C9, "ˉ", cb89), +_c(U+02CA, "ˊ", cb8a), +_c(U+02CB, "ˋ", cb8b), +_c(U+02CC, "ˌ", cb8c), +_c(U+02CD, "ˍ", cb8d), +_c(U+02CE, "ˎ", cb8e), +_c(U+02CF, "ˏ", cb8f), +_c(U+02D0, "ː", cb90), +_c(U+02D1, "ˑ", cb91), +_c(U+02D2, "˒", cb92), +_c(U+02D3, "˓", cb93), +_c(U+02D4, "˔", cb94), +_c(U+02D5, "˕", cb95), +_c(U+02D6, "˖", cb96), +_c(U+02D7, "˗", cb97), +_c(U+02D8, "˘", cb98), +_c(U+02D9, "˙", cb99), +_c(U+02DA, "˚", cb9a), +_c(U+02DB, "˛", cb9b), +_c(U+02DC, "˜", cb9c), +_c(U+02DD, "˝", cb9d), +_c(U+02DE, "˞", cb9e), +_c(U+02DF, "˟", cb9f), +_c(U+02E0, "ˠ", cba0), +_c(U+02E1, "ˡ", cba1), +_c(U+02E2, "ˢ", cba2), +_c(U+02E3, "ˣ", cba3), +_c(U+02E4, "ˤ", cba4), +_c(U+02E5, "˥", cba5), +_c(U+02E6, "˦", cba6), +_c(U+02E7, "˧", cba7), +_c(U+02E8, "˨", cba8), +_c(U+02E9, "˩", cba9), +_c(U+02EA, "˪", cbaa), +_c(U+02EB, "˫", cbab), +_c(U+02EC, "ˬ", cbac), +_c(U+02ED, "˭", cbad), +_c(U+02EE, "ˮ", cbae), +_c(U+02EF, "˯", cbaf), +_c(U+02F0, "˰", cbb0), +_c(U+02F1, "˱", cbb1), +_c(U+02F2, "˲", cbb2), +_c(U+02F3, "˳", cbb3), +_c(U+02F4, "˴", cbb4), +_c(U+02F5, "˵", cbb5), +_c(U+02F6, "˶", cbb6), +_c(U+02F7, "˷", cbb7), +_c(U+02F8, "˸", cbb8), +_c(U+02F9, "˹", cbb9), +_c(U+02FA, "˺", cbba), +_c(U+02FB, "˻", cbbb), +_c(U+02FC, "˼", cbbc), +_c(U+02FD, "˽", cbbd), +_c(U+02FE, "˾", cbbe), +//_c(U+02FF, "˿", cbbf), +//_c(U+0300, ̀"̀ ", cc80), +//_c(U+0301, ́" ", cc81), +//_c(U+0302, ̂" ", cc82), +//_c(U+0303, ̃" ", cc83), +//_c(U+0304, ̄" ", cc84), +//_c(U+0305, "̅" , cc85), +//_c(U+0306, ̆" ", cc86), +//_c(U+0307, ̇" ", cc87), +//_c(U+0308, ̈" ", cc88), +//_c(U+0309, ̉" ", cc89), +//_c(U+030A, ̊" ", cc8a), +//_c(U+030B, ̋" ", cc8b), +//_c(U+030C, ̌" ", cc8c), +//_c(U+030D, "̍" , cc8d), +//_c(U+030E, "̎" , cc8e), +//_c(U+030F, ̏" ", cc8f), +//_c(U+0310, "̐" , cc90), +//_c(U+0311, ̑" ", cc91), +//_c(U+0312, ̒" ", cc92), +//_c(U+0313, "̓" , cc93), +//_c(U+0314, "̔" , cc94), +//_c(U+0315, "̕" , cc95), +//_c(U+0316, "̖" , cc96), +//_c(U+0317, "̗" , cc97), +//_c(U+0318, "̘" , cc98), +//_c(U+0319, "̙" , cc99), +//_c(U+031A, "̚" , cc9a), +//_c(U+031B, ̛" ", cc9b), +//_c(U+031C, "̜" , cc9c), +//_c(U+031D, "̝" , cc9d), +//_c(U+031E, "̞" , cc9e), +_c(U+031F, "̟" , cc9f), +_c(U+0320, "̠" , cca0), +_c(U+0321, "̡" , cca1), +_c(U+0322, "̢" , cca2), +//_c(U+0323, ̣" ", cca3), +//_c(U+0324, ̤" ", cca4), +_c(U+0325, "̥" , cca5), +//_c(U+0326, ̦" ", cca6), +//_c(U+0327, ̧" ", cca7), +//_c(U+0328, ̨" ", cca8), +//_c(U+0329, "̩" , cca9), +_c(U+032A, "̪" , ccaa), +_c(U+032B, "̫" , ccab), +_c(U+032C, "̬" , ccac), +_c(U+032D, "̭" , ccad), +//_c(U+032E, ̮" ", ccae), +_c(U+032F, "̯" , ccaf), +_c(U+0330, "̰" , ccb0), +//_c(U+0331, ̱" ", ccb1), +_c(U+0332, "̲" , ccb2), +_c(U+0333, "̳" , ccb3), +_c(U+0334, "̴" , ccb4), +//_c(U+0335, ̵" ", ccb5), +//_c(U+0336, ̶" ", ccb6), +_c(U+0337, "̷" , ccb7), +_c(U+0338, "̸" , ccb8), +_c(U+0339, "̹" , ccb9), +_c(U+033A, "̺" , ccba), +_c(U+033B, "̻" , ccbb), +_c(U+033C, "̼" , ccbc), +_c(U+033D, "̽" , ccbd), +_c(U+033E, "̾" , ccbe), +_c(U+033F, "̿" , ccbf), +_c(U+0340, "̀" , cd80), +_c(U+0341, "́" , cd81), +_c(U+0342, "͂" , cd82), +_c(U+0343, "̓" , cd83), +_c(U+0344, "̈́" , cd84), +_c(U+0345, "ͅ" , cd85), +_c(U+0346, "͆" , cd86), +_c(U+0347, "͇" , cd87), +_c(U+0348, "͈" , cd88), +_c(U+0349, "͉" , cd89), +_c(U+034A, "͊" , cd8a), +_c(U+034B, "͋" , cd8b), +_c(U+034C, "͌" , cd8c), +_c(U+034D, "͍" , cd8d), +_c(U+034E, "͎" , cd8e), +_c(U+034F, "͏" , cd8f), +_c(U+0350, "͐" , cd90), +_c(U+0351, "͑" , cd91), +_c(U+0352, "͒" , cd92), +_c(U+0353, "͓" , cd93), +_c(U+0354, "͔" , cd94), +_c(U+0355, "͕" , cd95), +_c(U+0356, "͖" , cd96), +_c(U+0357, "͗" , cd97), +_c(U+0358, "͘" , cd98), +_c(U+0359, "͙" , cd99), +_c(U+035A, "͚" , cd9a), +_c(U+035B, "͛" , cd9b), +_c(U+035C, "͜" , cd9c), +_c(U+035D, "͝" , cd9d), +_c(U+035E, "͞" , cd9e), +_c(U+035F, "͟" , cd9f), +_c(U+0360, "͠" , cda0), +_c(U+0361, "͡" , cda1), +_c(U+0362, "͢" , cda2), +_c(U+0363, "ͣ" , cda3), +_c(U+0364, "ͤ" , cda4), +_c(U+0365, "ͥ" , cda5), +_c(U+0366, "ͦ" , cda6), +_c(U+0367, "ͧ" , cda7), +_c(U+0368, "ͨ" , cda8), +_c(U+0369, "ͩ" , cda9), +_c(U+036A, "ͪ" , cdaa), +_c(U+036B, "ͫ" , cdab), +_c(U+036C, "ͬ" , cdac), +_c(U+036D, "ͭ" , cdad), +_c(U+036E, "ͮ" , cdae), +_c(U+036F, "ͯ" , cdaf), +_c(U+0370, "Ͱ", cdb0), +_c(U+0371, "ͱ", cdb1), +_c(U+0372, "Ͳ", cdb2), +_c(U+0373, "ͳ", cdb3), +_c(U+0374, "ʹ", cdb4), +_c(U+0375, "͵", cdb5), +_c(U+0376, "Ͷ", cdb6), +_c(U+0377, "ͷ", cdb7), +_c(U+0378, "", cdb8), +_c(U+0379, "", cdb9), +_c(U+037A, "ͺ", cdba), +_c(U+037B, "ͻ", cdbb), +_c(U+037C, "ͼ", cdbc), +_c(U+037D, "ͽ", cdbd), +_c(U+037E, ";", cdbe), +_c(U+037F, "Ϳ", cdbf), +_c(U+0380, "", ce80), +_c(U+0381, "", ce81), +_c(U+0382, "", ce82), +_c(U+0383, "", ce83), +_c(U+0384, "΄", ce84), +_c(U+0385, "΅", ce85), +_c(U+0386, "Ά", ce86), +_c(U+0387, "·", ce87), +_c(U+0388, "Έ", ce88), +_c(U+0389, "Ή", ce89), +_c(U+038A, "Ί", ce8a), +_c(U+038B, "", ce8b), +_c(U+038C, "Ό", ce8c), +_c(U+038D, "", ce8d), +_c(U+038E, "Ύ", ce8e), +_c(U+038F, "Ώ", ce8f), +_c(U+0390, "ΐ", ce90), +_c(U+0391, "Α", ce91), +_c(U+0392, "Β", ce92), +_c(U+0393, "Γ", ce93), +_c(U+0394, "Δ", ce94), +_c(U+0395, "Ε", ce95), +_c(U+0396, "Ζ", ce96), +_c(U+0397, "Η", ce97), +_c(U+0398, "Θ", ce98), +_c(U+0399, "Ι", ce99), +_c(U+039A, "Κ", ce9a), +_c(U+039B, "Λ", ce9b), +_c(U+039C, "Μ", ce9c), +_c(U+039D, "Ν", ce9d), +_c(U+039E, "Ξ", ce9e), +_c(U+039F, "Ο", ce9f), +_c(U+03A0, "Π", cea0), +_c(U+03A1, "Ρ", cea1), +_c(U+03A2, "", cea2), +_c(U+03A3, "Σ", cea3), +_c(U+03A4, "Τ", cea4), +_c(U+03A5, "Υ", cea5), +_c(U+03A6, "Φ", cea6), +_c(U+03A7, "Χ", cea7), +_c(U+03A8, "Ψ", cea8), +_c(U+03A9, "Ω", cea9), +_c(U+03AA, "Ϊ", ceaa), +_c(U+03AB, "Ϋ", ceab), +_c(U+03AC, "ά", ceac), +_c(U+03AD, "έ", cead), +_c(U+03AE, "ή", ceae), +_c(U+03AF, "ί", ceaf), +_c(U+03B0, "ΰ", ceb0), +_c(U+03B1, "α", ceb1), +_c(U+03B2, "β", ceb2), +_c(U+03B3, "γ", ceb3), +_c(U+03B4, "δ", ceb4), +_c(U+03B5, "ε", ceb5), +_c(U+03B6, "ζ", ceb6), +_c(U+03B7, "η", ceb7), +_c(U+03B8, "θ", ceb8), +_c(U+03B9, "ι", ceb9), +_c(U+03BA, "κ", ceba), +_c(U+03BB, "λ", cebb), +_c(U+03BC, "μ", cebc), +_c(U+03BD, "ν", cebd), +_c(U+03BE, "ξ", cebe), +_c(U+03BF, "ο", cebf), +_c(U+03C0, "π", cf80), +_c(U+03C1, "ρ", cf81), +_c(U+03C2, "ς", cf82), +_c(U+03C3, "σ", cf83), +_c(U+03C4, "τ", cf84), +_c(U+03C5, "υ", cf85), +_c(U+03C6, "φ", cf86), +_c(U+03C7, "χ", cf87), +_c(U+03C8, "ψ", cf88), +_c(U+03C9, "ω", cf89), +_c(U+03CA, "ϊ", cf8a), +_c(U+03CB, "ϋ", cf8b), +_c(U+03CC, "ό", cf8c), +_c(U+03CD, "ύ", cf8d), +_c(U+03CE, "ώ", cf8e), +_c(U+03CF, "Ϗ", cf8f), +_c(U+03D0, "ϐ", cf90), +_c(U+03D1, "ϑ", cf91), +_c(U+03D2, "ϒ", cf92), +_c(U+03D3, "ϓ", cf93), +_c(U+03D4, "ϔ", cf94), +_c(U+03D5, "ϕ", cf95), +_c(U+03D6, "ϖ", cf96), +_c(U+03D7, "ϗ", cf97), +_c(U+03D8, "Ϙ", cf98), +_c(U+03D9, "ϙ", cf99), +_c(U+03DA, "Ϛ", cf9a), +_c(U+03DB, "ϛ", cf9b), +_c(U+03DC, "Ϝ", cf9c), +_c(U+03DD, "ϝ", cf9d), +_c(U+03DE, "Ϟ", cf9e), +_c(U+03DF, "ϟ", cf9f), +_c(U+03E0, "Ϡ", cfa0), +_c(U+03E1, "ϡ", cfa1), +_c(U+03E2, "Ϣ", cfa2), +_c(U+03E3, "ϣ", cfa3), +_c(U+03E4, "Ϥ", cfa4), +_c(U+03E5, "ϥ", cfa5), +_c(U+03E6, "Ϧ", cfa6), +_c(U+03E7, "ϧ", cfa7), +_c(U+03E8, "Ϩ", cfa8), +_c(U+03E9, "ϩ", cfa9), +_c(U+03EA, "Ϫ", cfaa), +_c(U+03EB, "ϫ", cfab), +_c(U+03EC, "Ϭ", cfac), +_c(U+03ED, "ϭ", cfad), +_c(U+03EE, "Ϯ", cfae), +_c(U+03EF, "ϯ", cfaf), +_c(U+03F0, "ϰ", cfb0), +_c(U+03F1, "ϱ", cfb1), +_c(U+03F2, "ϲ", cfb2), +_c(U+03F3, "ϳ", cfb3), +_c(U+03F4, "ϴ", cfb4), +_c(U+03F5, "ϵ", cfb5), +_c(U+03F6, "϶", cfb6), +_c(U+03F7, "Ϸ", cfb7), +_c(U+03F8, "ϸ", cfb8), +_c(U+03F9, "Ϲ", cfb9), +_c(U+03FA, "Ϻ", cfba), +_c(U+03FB, "ϻ", cfbb), +_c(U+03FC, "ϼ", cfbc), +_c(U+03FD, "Ͻ", cfbd), +_c(U+03FE, "Ͼ", cfbe), +_c(U+03FF, "Ͽ", cfbf), +_c(U+0400, "Ѐ", d080), +_c(U+0401, "Ё", d081), +_c(U+0402, "Ђ", d082), +_c(U+0403, "Ѓ", d083), +_c(U+0404, "Є", d084), +_c(U+0405, "Ѕ", d085), +_c(U+0406, "І", d086), +_c(U+0407, "Ї", d087), +_c(U+0408, "Ј", d088), +_c(U+0409, "Љ", d089), +_c(U+040A, "Њ", d08a), +_c(U+040B, "Ћ", d08b), +_c(U+040C, "Ќ", d08c), +_c(U+040D, "Ѝ", d08d), +_c(U+040E, "Ў", d08e), +_c(U+040F, "Џ", d08f), +_c(U+0410, "А", d090), +_c(U+0411, "Б", d091), +_c(U+0412, "В", d092), +_c(U+0413, "Г", d093), +_c(U+0414, "Д", d094), +_c(U+0415, "Е", d095), +_c(U+0416, "Ж", d096), +_c(U+0417, "З", d097), +_c(U+0418, "И", d098), +_c(U+0419, "Й", d099), +_c(U+041A, "К", d09a), +_c(U+041B, "Л", d09b), +_c(U+041C, "М", d09c), +_c(U+041D, "Н", d09d), +_c(U+041E, "О", d09e), +_c(U+041F, "П", d09f), +_c(U+0420, "Р", d0a0), +_c(U+0421, "С", d0a1), +_c(U+0422, "Т", d0a2), +_c(U+0423, "У", d0a3), +_c(U+0424, "Ф", d0a4), +_c(U+0425, "Х", d0a5), +_c(U+0426, "Ц", d0a6), +_c(U+0427, "Ч", d0a7), +_c(U+0428, "Ш", d0a8), +_c(U+0429, "Щ", d0a9), +_c(U+042A, "Ъ", d0aa), +_c(U+042B, "Ы", d0ab), +_c(U+042C, "Ь", d0ac), +_c(U+042D, "Э", d0ad), +_c(U+042E, "Ю", d0ae), +_c(U+042F, "Я", d0af), +_c(U+0430, "а", d0b0), +_c(U+0431, "б", d0b1), +_c(U+0432, "в", d0b2), +_c(U+0433, "г", d0b3), +_c(U+0434, "д", d0b4), +_c(U+0435, "е", d0b5), +_c(U+0436, "ж", d0b6), +_c(U+0437, "з", d0b7), +_c(U+0438, "и", d0b8), +_c(U+0439, "й", d0b9), +_c(U+043A, "к", d0ba), +_c(U+043B, "л", d0bb), +_c(U+043C, "м", d0bc), +_c(U+043D, "н", d0bd), +_c(U+043E, "о", d0be), +_c(U+043F, "п", d0bf), +_c(U+0440, "р", d180), +_c(U+0441, "с", d181), +_c(U+0442, "т", d182), +_c(U+0443, "у", d183), +_c(U+0444, "ф", d184), +_c(U+0445, "х", d185), +_c(U+0446, "ц", d186), +_c(U+0447, "ч", d187), +_c(U+0448, "ш", d188), +_c(U+0449, "щ", d189), +_c(U+044A, "ъ", d18a), +_c(U+044B, "ы", d18b), +_c(U+044C, "ь", d18c), +_c(U+044D, "э", d18d), +_c(U+044E, "ю", d18e), +_c(U+044F, "я", d18f), +_c(U+0450, "ѐ", d190), +_c(U+0451, "ё", d191), +_c(U+0452, "ђ", d192), +_c(U+0453, "ѓ", d193), +_c(U+0454, "є", d194), +_c(U+0455, "ѕ", d195), +_c(U+0456, "і", d196), +_c(U+0457, "ї", d197), +_c(U+0458, "ј", d198), +_c(U+0459, "љ", d199), +_c(U+045A, "њ", d19a), +_c(U+045B, "ћ", d19b), +_c(U+045C, "ќ", d19c), +_c(U+045D, "ѝ", d19d), +_c(U+045E, "ў", d19e), +_c(U+045F, "џ", d19f), +_c(U+0460, "Ѡ", d1a0), +_c(U+0461, "ѡ", d1a1), +_c(U+0462, "Ѣ", d1a2), +_c(U+0463, "ѣ", d1a3), +_c(U+0464, "Ѥ", d1a4), +_c(U+0465, "ѥ", d1a5), +_c(U+0466, "Ѧ", d1a6), +_c(U+0467, "ѧ", d1a7), +_c(U+0468, "Ѩ", d1a8), +_c(U+0469, "ѩ", d1a9), +_c(U+046A, "Ѫ", d1aa), +_c(U+046B, "ѫ", d1ab), +_c(U+046C, "Ѭ", d1ac), +_c(U+046D, "ѭ", d1ad), +_c(U+046E, "Ѯ", d1ae), +_c(U+046F, "ѯ", d1af), +_c(U+0470, "Ѱ", d1b0), +_c(U+0471, "ѱ", d1b1), +_c(U+0472, "Ѳ", d1b2), +_c(U+0473, "ѳ", d1b3), +_c(U+0474, "Ѵ", d1b4), +_c(U+0475, "ѵ", d1b5), +_c(U+0476, "Ѷ", d1b6), +_c(U+0477, "ѷ", d1b7), +_c(U+0478, "Ѹ", d1b8), +_c(U+0479, "ѹ", d1b9), +_c(U+047A, "Ѻ", d1ba), +_c(U+047B, "ѻ", d1bb), +_c(U+047C, "Ѽ", d1bc), +_c(U+047D, "ѽ", d1bd), +_c(U+047E, "Ѿ", d1be), +_c(U+047F, "ѿ", d1bf), +_c(U+20A0, "₠", e282a0), +_c(U+20A1, "₡", e282a1), +_c(U+20A2, "₢", e282a2), +_c(U+20A3, "₣", e282a3), +_c(U+20A4, "₤", e282a4), +_c(U+20A5, "₥", e282a5), +_c(U+20A6, "₦", e282a6), +_c(U+20A7, "₧", e282a7), +_c(U+20A8, "₨", e282a8), +_c(U+20A9, "₩", e282a9), +_c(U+20AA, "₪", e282aa), +_c(U+20AB, "₫", e282ab), +_c(U+20AC, "€", e282ac), +_c(U+20AD, "₭", e282ad), +_c(U+20AE, "₮", e282ae), +_c(U+20AF, "₯", e282af), +_c(U+20B0, "₰", e282b0), +_c(U+20B1, "₱", e282b1), +_c(U+20B2, "₲", e282b2), +_c(U+20B3, "₳", e282b3), +_c(U+20B4, "₴", e282b4), +_c(U+20B5, "₵", e282b5), +_c(U+20B6, "₶", e282b6), +_c(U+20B7, "₷", e282b7), +_c(U+20B8, "₸", e282b8), +_c(U+20B9, "₹", e282b9), +_c(U+20BA, "₺", e282ba), +_c(U+20BB, "₻", e282bb), +_c(U+20BC, "₼", e282bc), +_c(U+20BD, "₽", e282bd), +_c(U+20BE, "₾", e282be), +_c(U+20BF, "₿", e282bf), +_c(U+20C0, "⃀", e28380), +_c(U+20C1, "", e28381), +_c(U+20C2, "", e28382), +_c(U+20C3, "", e28383), +_c(U+20C4, "", e28384), +_c(U+20C5, "", e28385), +_c(U+20C6, "", e28386), +_c(U+20C7, "", e28387), +_c(U+20C8, "", e28388), +_c(U+20C9, "", e28389), +_c(U+20CA, "", e2838a), +_c(U+20CB, "", e2838b), +_c(U+20CC, "", e2838c), +_c(U+20CD, "", e2838d), +_c(U+20CE, "", e2838e), +_c(U+20CF, "", e2838f), +_c(U+20D0, "⃐", e28390), +_c(U+20D1, "⃑", e28391), +_c(U+20D2, "⃒", e28392), +_c(U+20D3, "⃓", e28393), +_c(U+20D4, "⃔", e28394), +_c(U+20D5, "⃕", e28395), +_c(U+20D6, "⃖", e28396), +_c(U+20D7, "⃗", e28397), +_c(U+20D8, "⃘", e28398), +_c(U+20D9, "⃙", e28399), +_c(U+20DA, "⃚", e2839a), +_c(U+20DB, "⃛", e2839b), +_c(U+20DC, "⃜", e2839c), +_c(U+20DD, "⃝", e2839d), +_c(U+20DE, "⃞", e2839e), +_c(U+20DF, "⃟", e2839f), +_c(U+20E0, "⃠", e283a0), +_c(U+20E1, "⃡", e283a1), +_c(U+20E2, "⃢", e283a2), +_c(U+20E3, "⃣", e283a3), +_c(U+20E4, "⃤", e283a4), +_c(U+20E5, "⃥", e283a5), +_c(U+20E6, "⃦", e283a6), +_c(U+20E7, "⃧", e283a7), +_c(U+20E8, "⃨", e283a8), +_c(U+20E9, "⃩", e283a9), +_c(U+20EA, "⃪", e283aa), +_c(U+20EB, "⃫", e283ab), +_c(U+20EC, "⃬", e283ac), +_c(U+20ED, "⃭", e283ad), +_c(U+20EE, "⃮", e283ae), +_c(U+20EF, "⃯", e283af), +_c(U+20F0, "⃰", e283b0), +_c(U+20F1, "", e283b1), +_c(U+20F2, "", e283b2), +_c(U+20F3, "", e283b3), +_c(U+20F4, "", e283b4), +_c(U+20F5, "", e283b5), +_c(U+20F6, "", e283b6), +_c(U+20F7, "", e283b7), +_c(U+20F8, "", e283b8), +_c(U+20F9, "", e283b9), +_c(U+20FA, "", e283ba), +_c(U+20FB, "", e283bb), +_c(U+20FC, "", e283bc), +_c(U+20FD, "", e283bd), +_c(U+20FE, "", e283be), +_c(U+20FF, "", e283bf), +_c(U+2100, "℀", e28480), +_c(U+2101, "℁", e28481), +_c(U+2102, "ℂ", e28482), +_c(U+2103, "℃", e28483), +_c(U+2104, "℄", e28484), +_c(U+2105, "℅", e28485), +_c(U+2106, "℆", e28486), +_c(U+2107, "ℇ", e28487), +_c(U+2108, "℈", e28488), +_c(U+2109, "℉", e28489), +_c(U+210A, "ℊ", e2848a), +_c(U+210B, "ℋ", e2848b), +_c(U+210C, "ℌ", e2848c), +_c(U+210D, "ℍ", e2848d), +_c(U+210E, "ℎ", e2848e), +_c(U+210F, "ℏ", e2848f), +_c(U+2110, "ℐ", e28490), +_c(U+2111, "ℑ", e28491), +_c(U+2112, "ℒ", e28492), +_c(U+2113, "ℓ", e28493), +_c(U+2114, "℔", e28494), +_c(U+2115, "ℕ", e28495), +_c(U+2116, "№", e28496), +_c(U+2117, "℗", e28497), +_c(U+2118, "℘", e28498), +_c(U+2119, "ℙ", e28499), +_c(U+211A, "ℚ", e2849a), +_c(U+211B, "ℛ", e2849b), +_c(U+211C, "ℜ", e2849c), +_c(U+211D, "ℝ", e2849d), +_c(U+211E, "℞", e2849e), +_c(U+211F, "℟", e2849f), +_c(U+2120, "℠", e284a0), +_c(U+2121, "℡", e284a1), +_c(U+2122, "™", e284a2), +_c(U+2123, "℣", e284a3), +_c(U+2124, "ℤ", e284a4), +_c(U+2125, "℥", e284a5), +_c(U+2126, "Ω", e284a6), +_c(U+2127, "℧", e284a7), +_c(U+2128, "ℨ", e284a8), +_c(U+2129, "℩", e284a9), +_c(U+212A, "K", e284aa), +_c(U+212B, "Å", e284ab), +_c(U+212C, "ℬ", e284ac), +_c(U+212D, "ℭ", e284ad), +_c(U+212E, "℮", e284ae), +_c(U+212F, "ℯ", e284af), +_c(U+2130, "ℰ", e284b0), +_c(U+2131, "ℱ", e284b1), +_c(U+2132, "Ⅎ", e284b2), +_c(U+2133, "ℳ", e284b3), +_c(U+2134, "ℴ", e284b4), +_c(U+2135, "ℵ", e284b5), +_c(U+2136, "ℶ", e284b6), +_c(U+2137, "ℷ", e284b7), +_c(U+2138, "ℸ", e284b8), +_c(U+2139, "ℹ", e284b9), +_c(U+213A, "℺", e284ba), +_c(U+213B, "℻", e284bb), +_c(U+213C, "ℼ", e284bc), +_c(U+213D, "ℽ", e284bd), +_c(U+213E, "ℾ", e284be), +_c(U+213F, "ℿ", e284bf), +_c(U+2140, "⅀", e28580), +_c(U+2141, "⅁", e28581), +_c(U+2142, "⅂", e28582), +_c(U+2143, "⅃", e28583), +_c(U+2144, "⅄", e28584), +_c(U+2145, "ⅅ", e28585), +_c(U+2146, "ⅆ", e28586), +_c(U+2147, "ⅇ", e28587), +_c(U+2148, "ⅈ", e28588), +_c(U+2149, "ⅉ", e28589), +_c(U+214A, "⅊", e2858a), +_c(U+214B, "⅋", e2858b), +_c(U+214C, "⅌", e2858c), +_c(U+214D, "⅍", e2858d), +_c(U+214E, "ⅎ", e2858e), +_c(U+214F, "⅏", e2858f), +_c(U+2150, "⅐", e28590), +_c(U+2151, "⅑", e28591), +_c(U+2152, "⅒", e28592), +_c(U+2153, "⅓", e28593), +_c(U+2154, "⅔", e28594), +_c(U+2155, "⅕", e28595), +_c(U+2156, "⅖", e28596), +_c(U+2157, "⅗", e28597), +_c(U+2158, "⅘", e28598), +_c(U+2159, "⅙", e28599), +_c(U+215A, "⅚", e2859a), +_c(U+215B, "⅛", e2859b), +_c(U+215C, "⅜", e2859c), +_c(U+215D, "⅝", e2859d), +_c(U+215E, "⅞", e2859e), +_c(U+215F, "⅟", e2859f), +_c(U+2160, "Ⅰ", e285a0), +_c(U+2161, "Ⅱ", e285a1), +_c(U+2162, "Ⅲ", e285a2), +_c(U+2163, "Ⅳ", e285a3), +_c(U+2164, "Ⅴ", e285a4), +_c(U+2165, "Ⅵ", e285a5), +_c(U+2166, "Ⅶ", e285a6), +_c(U+2167, "Ⅷ", e285a7), +_c(U+2168, "Ⅸ", e285a8), +_c(U+2169, "Ⅹ", e285a9), +_c(U+216A, "Ⅺ", e285aa), +_c(U+216B, "Ⅻ", e285ab), +_c(U+216C, "Ⅼ", e285ac), +_c(U+216D, "Ⅽ", e285ad), +_c(U+216E, "Ⅾ", e285ae), +_c(U+216F, "Ⅿ", e285af), +_c(U+2170, "ⅰ", e285b0), +_c(U+2171, "ⅱ", e285b1), +_c(U+2172, "ⅲ", e285b2), +_c(U+2173, "ⅳ", e285b3), +_c(U+2174, "ⅴ", e285b4), +_c(U+2175, "ⅵ", e285b5), +_c(U+2176, "ⅶ", e285b6), +_c(U+2177, "ⅷ", e285b7), +_c(U+2178, "ⅸ", e285b8), +_c(U+2179, "ⅹ", e285b9), +_c(U+217A, "ⅺ", e285ba), +_c(U+217B, "ⅻ", e285bb), +_c(U+217C, "ⅼ", e285bc), +_c(U+217D, "ⅽ", e285bd), +_c(U+217E, "ⅾ", e285be), +_c(U+217F, "ⅿ", e285bf), +_c(U+2180, "ↀ", e28680), +_c(U+2181, "ↁ", e28681), +_c(U+2182, "ↂ", e28682), +_c(U+2183, "Ↄ", e28683), +_c(U+2184, "ↄ", e28684), +_c(U+2185, "ↅ", e28685), +_c(U+2186, "ↆ", e28686), +_c(U+2187, "ↇ", e28687), +_c(U+2188, "ↈ", e28688), +_c(U+2189, "↉", e28689), +_c(U+218A, "↊", e2868a), +_c(U+218B, "↋", e2868b), +_c(U+218C, "", e2868c), +_c(U+218D, "", e2868d), +_c(U+218E, "", e2868e), +_c(U+218F, "", e2868f), +_c(U+2190, "←", e28690), +_c(U+2191, "↑", e28691), +_c(U+2192, "→", e28692), +_c(U+2193, "↓", e28693), +_c(U+2194, "↔", e28694), +_c(U+2195, "↕", e28695), +_c(U+2196, "↖", e28696), +_c(U+2197, "↗", e28697), +_c(U+2198, "↘", e28698), +_c(U+2199, "↙", e28699), +_c(U+219A, "↚", e2869a), +_c(U+219B, "↛", e2869b), +_c(U+219C, "↜", e2869c), +_c(U+219D, "↝", e2869d), +_c(U+219E, "↞", e2869e), +_c(U+219F, "↟", e2869f), +_c(U+21A0, "↠", e286a0), +_c(U+21A1, "↡", e286a1), +_c(U+21A2, "↢", e286a2), +_c(U+21A3, "↣", e286a3), +_c(U+21A4, "↤", e286a4), +_c(U+21A5, "↥", e286a5), +_c(U+21A6, "↦", e286a6), +_c(U+21A7, "↧", e286a7), +_c(U+21A8, "↨", e286a8), +_c(U+21A9, "↩", e286a9), +_c(U+21AA, "↪", e286aa), +_c(U+21AB, "↫", e286ab), +_c(U+21AC, "↬", e286ac), +_c(U+21AD, "↭", e286ad), +_c(U+21AE, "↮", e286ae), +_c(U+21AF, "↯", e286af), +_c(U+21B0, "↰", e286b0), +_c(U+21B1, "↱", e286b1), +_c(U+21B2, "↲", e286b2), +_c(U+21B3, "↳", e286b3), +_c(U+21B4, "↴", e286b4), +_c(U+21B5, "↵", e286b5), +_c(U+21B6, "↶", e286b6), +_c(U+21B7, "↷", e286b7), +_c(U+21B8, "↸", e286b8), +_c(U+21B9, "↹", e286b9), +_c(U+21BA, "↺", e286ba), +_c(U+21BB, "↻", e286bb), +_c(U+21BC, "↼", e286bc), +_c(U+21BD, "↽", e286bd), +_c(U+21BE, "↾", e286be), +_c(U+21BF, "↿", e286bf), +_c(U+21C0, "⇀", e28780), +_c(U+21C1, "⇁", e28781), +_c(U+21C2, "⇂", e28782), +_c(U+21C3, "⇃", e28783), +_c(U+21C4, "⇄", e28784), +_c(U+21C5, "⇅", e28785), +_c(U+21C6, "⇆", e28786), +_c(U+21C7, "⇇", e28787), +_c(U+21C8, "⇈", e28788), +_c(U+21C9, "⇉", e28789), +_c(U+21CA, "⇊", e2878a), +_c(U+21CB, "⇋", e2878b), +_c(U+21CC, "⇌", e2878c), +_c(U+21CD, "⇍", e2878d), +_c(U+21CE, "⇎", e2878e), +_c(U+21CF, "⇏", e2878f), +_c(U+21D0, "⇐", e28790), +_c(U+21D1, "⇑", e28791), +_c(U+21D2, "⇒", e28792), +_c(U+21D3, "⇓", e28793), +_c(U+21D4, "⇔", e28794), +_c(U+21D5, "⇕", e28795), +_c(U+21D6, "⇖", e28796), +_c(U+21D7, "⇗", e28797), +_c(U+21D8, "⇘", e28798), +_c(U+21D9, "⇙", e28799), +_c(U+21DA, "⇚", e2879a), +_c(U+21DB, "⇛", e2879b), +_c(U+21DC, "⇜", e2879c), +_c(U+21DD, "⇝", e2879d), +_c(U+21DE, "⇞", e2879e), +_c(U+21DF, "⇟", e2879f), +_c(U+21E0, "⇠", e287a0), +_c(U+21E1, "⇡", e287a1), +_c(U+21E2, "⇢", e287a2), +_c(U+21E3, "⇣", e287a3), +_c(U+21E4, "⇤", e287a4), +_c(U+21E5, "⇥", e287a5), +_c(U+21E6, "⇦", e287a6), +_c(U+21E7, "⇧", e287a7), +_c(U+21E8, "⇨", e287a8), +_c(U+21E9, "⇩", e287a9), +_c(U+21EA, "⇪", e287aa), +_c(U+21EB, "⇫", e287ab), +_c(U+21EC, "⇬", e287ac), +_c(U+21ED, "⇭", e287ad), +_c(U+21EE, "⇮", e287ae), +_c(U+21EF, "⇯", e287af), +_c(U+21F0, "⇰", e287b0), +_c(U+21F1, "⇱", e287b1), +_c(U+21F2, "⇲", e287b2), +_c(U+21F3, "⇳", e287b3), +_c(U+21F4, "⇴", e287b4), +_c(U+21F5, "⇵", e287b5), +_c(U+21F6, "⇶", e287b6), +_c(U+21F7, "⇷", e287b7), +_c(U+21F8, "⇸", e287b8), +_c(U+21F9, "⇹", e287b9), +_c(U+21FA, "⇺", e287ba), +_c(U+21FB, "⇻", e287bb), +_c(U+21FC, "⇼", e287bc), +_c(U+21FD, "⇽", e287bd), +_c(U+21FE, "⇾", e287be), +_c(U+21FF, "⇿", e287bf), +_c(U+2200, "∀", e28880), +_c(U+2201, "∁", e28881), +_c(U+2202, "∂", e28882), +_c(U+2203, "∃", e28883), +_c(U+2204, "∄", e28884), +_c(U+2205, "∅", e28885), +_c(U+2206, "∆", e28886), +_c(U+2207, "∇", e28887), +_c(U+2208, "∈", e28888), +_c(U+2209, "∉", e28889), +_c(U+220A, "∊", e2888a), +_c(U+220B, "∋", e2888b), +_c(U+220C, "∌", e2888c), +_c(U+220D, "∍", e2888d), +_c(U+220E, "∎", e2888e), +_c(U+220F, "∏", e2888f), +_c(U+2210, "∐", e28890), +_c(U+2211, "∑", e28891), +_c(U+2212, "−", e28892), +_c(U+2213, "∓", e28893), +_c(U+2214, "∔", e28894), +_c(U+2215, "∕", e28895), +_c(U+2216, "∖", e28896), +_c(U+2217, "∗", e28897), +_c(U+2218, "∘", e28898), +_c(U+2219, "∙", e28899), +_c(U+221A, "√", e2889a), +_c(U+221B, "∛", e2889b), +_c(U+221C, "∜", e2889c), +_c(U+221D, "∝", e2889d), +_c(U+221E, "∞", e2889e), +_c(U+221F, "∟", e2889f), +_c(U+2220, "∠", e288a0), +_c(U+2221, "∡", e288a1), +_c(U+2222, "∢", e288a2), +_c(U+2223, "∣", e288a3), +_c(U+2224, "∤", e288a4), +_c(U+2225, "∥", e288a5), +_c(U+2226, "∦", e288a6), +_c(U+2227, "∧", e288a7), +_c(U+2228, "∨", e288a8), +_c(U+2229, "∩", e288a9), +_c(U+222A, "∪", e288aa), +_c(U+222B, "∫", e288ab), +_c(U+222C, "∬", e288ac), +_c(U+222D, "∭", e288ad), +_c(U+222E, "∮", e288ae), +_c(U+222F, "∯", e288af), +_c(U+2230, "∰", e288b0), +_c(U+2231, "∱", e288b1), +_c(U+2232, "∲", e288b2), +_c(U+2233, "∳", e288b3), +_c(U+2234, "∴", e288b4), +_c(U+2235, "∵", e288b5), +_c(U+2236, "∶", e288b6), +_c(U+2237, "∷", e288b7), +_c(U+2238, "∸", e288b8), +_c(U+2239, "∹", e288b9), +_c(U+223A, "∺", e288ba), +_c(U+223B, "∻", e288bb), +_c(U+223C, "∼", e288bc), +_c(U+223D, "∽", e288bd), +_c(U+223E, "∾", e288be), +_c(U+223F, "∿", e288bf), +_c(U+2240, "≀", e28980), +_c(U+2241, "≁", e28981), +_c(U+2242, "≂", e28982), +_c(U+2243, "≃", e28983), +_c(U+2244, "≄", e28984), +_c(U+2245, "≅", e28985), +_c(U+2246, "≆", e28986), +_c(U+2247, "≇", e28987), +_c(U+2248, "≈", e28988), +_c(U+2249, "≉", e28989), +_c(U+224A, "≊", e2898a), +_c(U+224B, "≋", e2898b), +_c(U+224C, "≌", e2898c), +_c(U+224D, "≍", e2898d), +_c(U+224E, "≎", e2898e), +_c(U+224F, "≏", e2898f), +_c(U+2250, "≐", e28990), +_c(U+2251, "≑", e28991), +_c(U+2252, "≒", e28992), +_c(U+2253, "≓", e28993), +_c(U+2254, "≔", e28994), +_c(U+2255, "≕", e28995), +_c(U+2256, "≖", e28996), +_c(U+2257, "≗", e28997), +_c(U+2258, "≘", e28998), +_c(U+2259, "≙", e28999), +_c(U+225A, "≚", e2899a), +_c(U+225B, "≛", e2899b), +_c(U+225C, "≜", e2899c), +_c(U+225D, "≝", e2899d), +_c(U+225E, "≞", e2899e), +_c(U+225F, "≟", e2899f), +_c(U+2260, "≠", e289a0), +_c(U+2261, "≡", e289a1), +_c(U+2262, "≢", e289a2), +_c(U+2263, "≣", e289a3), +_c(U+2264, "≤", e289a4), +_c(U+2265, "≥", e289a5), +_c(U+2266, "≦", e289a6), +_c(U+2267, "≧", e289a7), +_c(U+2268, "≨", e289a8), +_c(U+2269, "≩", e289a9), +_c(U+226A, "≪", e289aa), +_c(U+226B, "≫", e289ab), +_c(U+226C, "≬", e289ac), +_c(U+226D, "≭", e289ad), +_c(U+226E, "≮", e289ae), +_c(U+226F, "≯", e289af), +_c(U+2270, "≰", e289b0), +_c(U+2271, "≱", e289b1), +_c(U+2272, "≲", e289b2), +_c(U+2273, "≳", e289b3), +_c(U+2274, "≴", e289b4), +_c(U+2275, "≵", e289b5), +_c(U+2276, "≶", e289b6), +_c(U+2277, "≷", e289b7), +_c(U+2278, "≸", e289b8), +_c(U+2279, "≹", e289b9), +_c(U+227A, "≺", e289ba), +_c(U+227B, "≻", e289bb), +_c(U+227C, "≼", e289bc), +_c(U+227D, "≽", e289bd), +_c(U+227E, "≾", e289be), +_c(U+227F, "≿", e289bf), +_c(U+2280, "⊀", e28a80), +_c(U+2281, "⊁", e28a81), +_c(U+2282, "⊂", e28a82), +_c(U+2283, "⊃", e28a83), +_c(U+2284, "⊄", e28a84), +_c(U+2285, "⊅", e28a85), +_c(U+2286, "⊆", e28a86), +_c(U+2287, "⊇", e28a87), +_c(U+2288, "⊈", e28a88), +_c(U+2289, "⊉", e28a89), +_c(U+228A, "⊊", e28a8a), +_c(U+228B, "⊋", e28a8b), +_c(U+228C, "⊌", e28a8c), +_c(U+228D, "⊍", e28a8d), +_c(U+228E, "⊎", e28a8e), +_c(U+228F, "⊏", e28a8f), +_c(U+2290, "⊐", e28a90), +_c(U+2291, "⊑", e28a91), +_c(U+2292, "⊒", e28a92), +_c(U+2293, "⊓", e28a93), +_c(U+2294, "⊔", e28a94), +_c(U+2295, "⊕", e28a95), +_c(U+2296, "⊖", e28a96), +_c(U+2297, "⊗", e28a97), +_c(U+2298, "⊘", e28a98), +_c(U+2299, "⊙", e28a99), +_c(U+229A, "⊚", e28a9a), +_c(U+229B, "⊛", e28a9b), +_c(U+229C, "⊜", e28a9c), +_c(U+229D, "⊝", e28a9d), +_c(U+229E, "⊞", e28a9e), +_c(U+229F, "⊟", e28a9f), +_c(U+22A0, "⊠", e28aa0), +_c(U+22A1, "⊡", e28aa1), +_c(U+22A2, "⊢", e28aa2), +_c(U+22A3, "⊣", e28aa3), +_c(U+22A4, "⊤", e28aa4), +_c(U+22A5, "⊥", e28aa5), +_c(U+22A6, "⊦", e28aa6), +_c(U+22A7, "⊧", e28aa7), +_c(U+22A8, "⊨", e28aa8), +_c(U+22A9, "⊩", e28aa9), +_c(U+22AA, "⊪", e28aaa), +_c(U+22AB, "⊫", e28aab), +_c(U+22AC, "⊬", e28aac), +_c(U+22AD, "⊭", e28aad), +_c(U+22AE, "⊮", e28aae), +_c(U+22AF, "⊯", e28aaf), +_c(U+22B0, "⊰", e28ab0), +_c(U+22B1, "⊱", e28ab1), +_c(U+22B2, "⊲", e28ab2), +_c(U+22B3, "⊳", e28ab3), +_c(U+22B4, "⊴", e28ab4), +_c(U+22B5, "⊵", e28ab5), +_c(U+22B6, "⊶", e28ab6), +_c(U+22B7, "⊷", e28ab7), +_c(U+22B8, "⊸", e28ab8), +_c(U+22B9, "⊹", e28ab9), +_c(U+22BA, "⊺", e28aba), +_c(U+22BB, "⊻", e28abb), +_c(U+22BC, "⊼", e28abc), +_c(U+22BD, "⊽", e28abd), +_c(U+22BE, "⊾", e28abe), +_c(U+22BF, "⊿", e28abf), +_c(U+22C0, "⋀", e28b80), +_c(U+22C1, "⋁", e28b81), +_c(U+22C2, "⋂", e28b82), +_c(U+22C3, "⋃", e28b83), +_c(U+22C4, "⋄", e28b84), +_c(U+22C5, "⋅", e28b85), +_c(U+22C6, "⋆", e28b86), +_c(U+22C7, "⋇", e28b87), +_c(U+22C8, "⋈", e28b88), +_c(U+22C9, "⋉", e28b89), +_c(U+22CA, "⋊", e28b8a), +_c(U+22CB, "⋋", e28b8b), +_c(U+22CC, "⋌", e28b8c), +_c(U+22CD, "⋍", e28b8d), +_c(U+22CE, "⋎", e28b8e), +_c(U+22CF, "⋏", e28b8f), +_c(U+22D0, "⋐", e28b90), +_c(U+22D1, "⋑", e28b91), +_c(U+22D2, "⋒", e28b92), +_c(U+22D3, "⋓", e28b93), +_c(U+22D4, "⋔", e28b94), +_c(U+22D5, "⋕", e28b95), +_c(U+22D6, "⋖", e28b96), +_c(U+22D7, "⋗", e28b97), +_c(U+22D8, "⋘", e28b98), +_c(U+22D9, "⋙", e28b99), +_c(U+22DA, "⋚", e28b9a), +_c(U+22DB, "⋛", e28b9b), +_c(U+22DC, "⋜", e28b9c), +_c(U+22DD, "⋝", e28b9d), +_c(U+22DE, "⋞", e28b9e), +_c(U+22DF, "⋟", e28b9f), +_c(U+22E0, "⋠", e28ba0), +_c(U+22E1, "⋡", e28ba1), +_c(U+22E2, "⋢", e28ba2), +_c(U+22E3, "⋣", e28ba3), +_c(U+22E4, "⋤", e28ba4), +_c(U+22E5, "⋥", e28ba5), +_c(U+22E6, "⋦", e28ba6), +_c(U+22E7, "⋧", e28ba7), +_c(U+22E8, "⋨", e28ba8), +_c(U+22E9, "⋩", e28ba9), +_c(U+22EA, "⋪", e28baa), +_c(U+22EB, "⋫", e28bab), +_c(U+22EC, "⋬", e28bac), +_c(U+22ED, "⋭", e28bad), +_c(U+22EE, "⋮", e28bae), +_c(U+22EF, "⋯", e28baf), +_c(U+22F0, "⋰", e28bb0), +_c(U+22F1, "⋱", e28bb1), +_c(U+22F2, "⋲", e28bb2), +_c(U+22F3, "⋳", e28bb3), +_c(U+22F4, "⋴", e28bb4), +_c(U+22F5, "⋵", e28bb5), +_c(U+22F6, "⋶", e28bb6), +_c(U+22F7, "⋷", e28bb7), +_c(U+22F8, "⋸", e28bb8), +_c(U+22F9, "⋹", e28bb9), +_c(U+22FA, "⋺", e28bba), +_c(U+22FB, "⋻", e28bbb), +_c(U+22FC, "⋼", e28bbc), +_c(U+22FD, "⋽", e28bbd), +_c(U+22FE, "⋾", e28bbe), +_c(U+22FF, "⋿", e28bbf), +_c(U+2300, "⌀", e28c80), +_c(U+2301, "⌁", e28c81), +_c(U+2302, "⌂", e28c82), +_c(U+2303, "⌃", e28c83), +_c(U+2304, "⌄", e28c84), +_c(U+2305, "⌅", e28c85), +_c(U+2306, "⌆", e28c86), +_c(U+2307, "⌇", e28c87), +_c(U+2308, "⌈", e28c88), +_c(U+2309, "⌉", e28c89), +_c(U+230A, "⌊", e28c8a), +_c(U+230B, "⌋", e28c8b), +_c(U+230C, "⌌", e28c8c), +_c(U+230D, "⌍", e28c8d), +_c(U+230E, "⌎", e28c8e), +_c(U+230F, "⌏", e28c8f), +_c(U+2310, "⌐", e28c90), +_c(U+2311, "⌑", e28c91), +_c(U+2312, "⌒", e28c92), +_c(U+2313, "⌓", e28c93), +_c(U+2314, "⌔", e28c94), +_c(U+2315, "⌕", e28c95), +_c(U+2316, "⌖", e28c96), +_c(U+2317, "⌗", e28c97), +_c(U+2318, "⌘", e28c98), +_c(U+2319, "⌙", e28c99), +_c(U+231A, "⌚", e28c9a), +_c(U+231B, "⌛", e28c9b), +_c(U+231C, "⌜", e28c9c), +_c(U+231D, "⌝", e28c9d), +_c(U+231E, "⌞", e28c9e), +_c(U+231F, "⌟", e28c9f), +_c(U+2320, "⌠", e28ca0), +_c(U+2321, "⌡", e28ca1), +_c(U+2322, "⌢", e28ca2), +_c(U+2323, "⌣", e28ca3), +_c(U+2324, "⌤", e28ca4), +_c(U+2325, "⌥", e28ca5), +_c(U+2326, "⌦", e28ca6), +_c(U+2327, "⌧", e28ca7), +_c(U+2328, "⌨", e28ca8), +_c(U+2329, "〈", e28ca9), +_c(U+232A, "〉", e28caa), +_c(U+232B, "⌫", e28cab), +_c(U+232C, "⌬", e28cac), +_c(U+232D, "⌭", e28cad), +_c(U+232E, "⌮", e28cae), +_c(U+232F, "⌯", e28caf), +_c(U+2330, "⌰", e28cb0), +_c(U+2331, "⌱", e28cb1), +_c(U+2332, "⌲", e28cb2), +_c(U+2333, "⌳", e28cb3), +_c(U+2334, "⌴", e28cb4), +_c(U+2335, "⌵", e28cb5), +_c(U+2336, "⌶", e28cb6), +_c(U+2337, "⌷", e28cb7), +_c(U+2338, "⌸", e28cb8), +_c(U+2339, "⌹", e28cb9), +_c(U+233A, "⌺", e28cba), +_c(U+233B, "⌻", e28cbb), +_c(U+233C, "⌼", e28cbc), +_c(U+233D, "⌽", e28cbd), +_c(U+233E, "⌾", e28cbe), +_c(U+233F, "⌿", e28cbf), +_c(U+2340, "⍀", e28d80), +_c(U+2341, "⍁", e28d81), +_c(U+2342, "⍂", e28d82), +_c(U+2343, "⍃", e28d83), +_c(U+2344, "⍄", e28d84), +_c(U+2345, "⍅", e28d85), +_c(U+2346, "⍆", e28d86), +_c(U+2347, "⍇", e28d87), +_c(U+2348, "⍈", e28d88), +_c(U+2349, "⍉", e28d89), +_c(U+234A, "⍊", e28d8a), +_c(U+234B, "⍋", e28d8b), +_c(U+234C, "⍌", e28d8c), +_c(U+234D, "⍍", e28d8d), +_c(U+234E, "⍎", e28d8e), +_c(U+234F, "⍏", e28d8f), +_c(U+2350, "⍐", e28d90), +_c(U+2351, "⍑", e28d91), +_c(U+2352, "⍒", e28d92), +_c(U+2353, "⍓", e28d93), +_c(U+2354, "⍔", e28d94), +_c(U+2355, "⍕", e28d95), +_c(U+2356, "⍖", e28d96), +_c(U+2357, "⍗", e28d97), +_c(U+2358, "⍘", e28d98), +_c(U+2359, "⍙", e28d99), +_c(U+235A, "⍚", e28d9a), +_c(U+235B, "⍛", e28d9b), +_c(U+235C, "⍜", e28d9c), +_c(U+235D, "⍝", e28d9d), +_c(U+235E, "⍞", e28d9e), +_c(U+235F, "⍟", e28d9f), +_c(U+2360, "⍠", e28da0), +_c(U+2361, "⍡", e28da1), +_c(U+2362, "⍢", e28da2), +_c(U+2363, "⍣", e28da3), +_c(U+2364, "⍤", e28da4), +_c(U+2365, "⍥", e28da5), +_c(U+2366, "⍦", e28da6), +_c(U+2367, "⍧", e28da7), +_c(U+2368, "⍨", e28da8), +_c(U+2369, "⍩", e28da9), +_c(U+236A, "⍪", e28daa), +_c(U+236B, "⍫", e28dab), +_c(U+236C, "⍬", e28dac), +_c(U+236D, "⍭", e28dad), +_c(U+236E, "⍮", e28dae), +_c(U+236F, "⍯", e28daf), +_c(U+2370, "⍰", e28db0), +_c(U+2371, "⍱", e28db1), +_c(U+2372, "⍲", e28db2), +_c(U+2373, "⍳", e28db3), +_c(U+2374, "⍴", e28db4), +_c(U+2375, "⍵", e28db5), +_c(U+2376, "⍶", e28db6), +_c(U+2377, "⍷", e28db7), +_c(U+2378, "⍸", e28db8), +_c(U+2379, "⍹", e28db9), +_c(U+237A, "⍺", e28dba), +_c(U+237B, "⍻", e28dbb), +_c(U+237C, "⍼", e28dbc), +_c(U+237D, "⍽", e28dbd), +_c(U+237E, "⍾", e28dbe), +_c(U+237F, "⍿", e28dbf), +_c(U+2380, "⎀", e28e80), +_c(U+2381, "⎁", e28e81), +_c(U+2382, "⎂", e28e82), +_c(U+2383, "⎃", e28e83), +_c(U+2384, "⎄", e28e84), +_c(U+2385, "⎅", e28e85), +_c(U+2386, "⎆", e28e86), +_c(U+2387, "⎇", e28e87), +_c(U+2388, "⎈", e28e88), +_c(U+2389, "⎉", e28e89), +_c(U+238A, "⎊", e28e8a), +_c(U+238B, "⎋", e28e8b), +_c(U+238C, "⎌", e28e8c), +_c(U+238D, "⎍", e28e8d), +_c(U+238E, "⎎", e28e8e), +_c(U+238F, "⎏", e28e8f), +_c(U+2390, "⎐", e28e90), +_c(U+2391, "⎑", e28e91), +_c(U+2392, "⎒", e28e92), +_c(U+2393, "⎓", e28e93), +_c(U+2394, "⎔", e28e94), +_c(U+2395, "⎕", e28e95), +_c(U+2396, "⎖", e28e96), +_c(U+2397, "⎗", e28e97), +_c(U+2398, "⎘", e28e98), +_c(U+2399, "⎙", e28e99), +_c(U+239A, "⎚", e28e9a), +_c(U+239B, "⎛", e28e9b), +_c(U+239C, "⎜", e28e9c), +_c(U+239D, "⎝", e28e9d), +_c(U+239E, "⎞", e28e9e), +_c(U+239F, "⎟", e28e9f), +_c(U+23A0, "⎠", e28ea0), +_c(U+23A1, "⎡", e28ea1), +_c(U+23A2, "⎢", e28ea2), +_c(U+23A3, "⎣", e28ea3), +_c(U+23A4, "⎤", e28ea4), +_c(U+23A5, "⎥", e28ea5), +_c(U+23A6, "⎦", e28ea6), +_c(U+23A7, "⎧", e28ea7), +_c(U+23A8, "⎨", e28ea8), +_c(U+23A9, "⎩", e28ea9), +_c(U+23AA, "⎪", e28eaa), +_c(U+23AB, "⎫", e28eab), +_c(U+23AC, "⎬", e28eac), +_c(U+23AD, "⎭", e28ead), +_c(U+23AE, "⎮", e28eae), +_c(U+23AF, "⎯", e28eaf), +_c(U+23B0, "⎰", e28eb0), +_c(U+23B1, "⎱", e28eb1), +_c(U+23B2, "⎲", e28eb2), +_c(U+23B3, "⎳", e28eb3), +_c(U+23B4, "⎴", e28eb4), +_c(U+23B5, "⎵", e28eb5), +_c(U+23B6, "⎶", e28eb6), +_c(U+23B7, "⎷", e28eb7), +_c(U+23B8, "⎸", e28eb8), +_c(U+23B9, "⎹", e28eb9), +_c(U+23BA, "⎺", e28eba), +_c(U+23BB, "⎻", e28ebb), +_c(U+23BC, "⎼", e28ebc), +_c(U+23BD, "⎽", e28ebd), +_c(U+23BE, "⎾", e28ebe), +_c(U+23BF, "⎿", e28ebf), +_c(U+23C0, "⏀", e28f80), +_c(U+23C1, "⏁", e28f81), +_c(U+23C2, "⏂", e28f82), +_c(U+23C3, "⏃", e28f83), +_c(U+23C4, "⏄", e28f84), +_c(U+23C5, "⏅", e28f85), +_c(U+23C6, "⏆", e28f86), +_c(U+23C7, "⏇", e28f87), +_c(U+23C8, "⏈", e28f88), +_c(U+23C9, "⏉", e28f89), +_c(U+23CA, "⏊", e28f8a), +_c(U+23CB, "⏋", e28f8b), +_c(U+23CC, "⏌", e28f8c), +_c(U+23CD, "⏍", e28f8d), +_c(U+23CE, "⏎", e28f8e), +_c(U+23CF, "⏏", e28f8f), +_c(U+23D0, "⏐", e28f90), +_c(U+23D1, "⏑", e28f91), +_c(U+23D2, "⏒", e28f92), +_c(U+23D3, "⏓", e28f93), +_c(U+23D4, "⏔", e28f94), +_c(U+23D5, "⏕", e28f95), +_c(U+23D6, "⏖", e28f96), +_c(U+23D7, "⏗", e28f97), +_c(U+23D8, "⏘", e28f98), +_c(U+23D9, "⏙", e28f99), +_c(U+23DA, "⏚", e28f9a), +_c(U+23DB, "⏛", e28f9b), +_c(U+23DC, "⏜", e28f9c), +_c(U+23DD, "⏝", e28f9d), +_c(U+23DE, "⏞", e28f9e), +_c(U+23DF, "⏟", e28f9f), +_c(U+23E0, "⏠", e28fa0), +_c(U+23E1, "⏡", e28fa1), +_c(U+23E2, "⏢", e28fa2), +_c(U+23E3, "⏣", e28fa3), +_c(U+23E4, "⏤", e28fa4), +_c(U+23E5, "⏥", e28fa5), +_c(U+23E6, "⏦", e28fa6), +_c(U+23E7, "⏧", e28fa7), +_c(U+23E8, "⏨", e28fa8), +_c(U+23E9, "⏩", e28fa9), +_c(U+23EA, "⏪", e28faa), +_c(U+23EB, "⏫", e28fab), +_c(U+23EC, "⏬", e28fac), +_c(U+23ED, "⏭", e28fad), +_c(U+23EE, "⏮", e28fae), +_c(U+23EF, "⏯", e28faf), +_c(U+23F0, "⏰", e28fb0), +_c(U+23F1, "⏱", e28fb1), +_c(U+23F2, "⏲", e28fb2), +_c(U+23F3, "⏳", e28fb3), +_c(U+23F4, "⏴", e28fb4), +_c(U+23F5, "⏵", e28fb5), +_c(U+23F6, "⏶", e28fb6), +_c(U+23F7, "⏷", e28fb7), +_c(U+23F8, "⏸", e28fb8), +_c(U+23F9, "⏹", e28fb9), +_c(U+23FA, "⏺", e28fba), +_c(U+23FB, "⏻", e28fbb), +_c(U+23FC, "⏼", e28fbc), +_c(U+23FD, "⏽", e28fbd), +_c(U+23FE, "⏾", e28fbe), +_c(U+23FF, "⏿", e28fbf), +_c(U+2400, "␀", e29080), +_c(U+2401, "␁", e29081), +_c(U+2402, "␂", e29082), +_c(U+2403, "␃", e29083), +_c(U+2404, "␄", e29084), +_c(U+2405, "␅", e29085), +_c(U+2406, "␆", e29086), +_c(U+2407, "␇", e29087), +_c(U+2408, "␈", e29088), +_c(U+2409, "␉", e29089), +_c(U+240A, "␊", e2908a), +_c(U+240B, "␋", e2908b), +_c(U+240C, "␌", e2908c), +_c(U+240D, "␍", e2908d), +_c(U+240E, "␎", e2908e), +_c(U+240F, "␏", e2908f), +_c(U+2410, "␐", e29090), +_c(U+2411, "␑", e29091), +_c(U+2412, "␒", e29092), +_c(U+2413, "␓", e29093), +_c(U+2414, "␔", e29094), +_c(U+2415, "␕", e29095), +_c(U+2416, "␖", e29096), +_c(U+2417, "␗", e29097), +_c(U+2418, "␘", e29098), +_c(U+2419, "␙", e29099), +_c(U+241A, "␚", e2909a), +_c(U+241B, "␛", e2909b), +_c(U+241C, "␜", e2909c), +_c(U+241D, "␝", e2909d), +_c(U+241E, "␞", e2909e), +_c(U+241F, "␟", e2909f), +_c(U+2420, "␠", e290a0), +_c(U+2421, "␡", e290a1), +_c(U+2422, "␢", e290a2), +_c(U+2423, "␣", e290a3), +_c(U+2424, "", e290a4), +_c(U+2425, "␥", e290a5), +_c(U+2426, "␦", e290a6), +_c(U+2427, "", e290a7), +_c(U+2428, "", e290a8), +_c(U+2429, "", e290a9), +_c(U+242A, "", e290aa), +_c(U+242B, "", e290ab), +_c(U+242C, "", e290ac), +_c(U+242D, "", e290ad), +_c(U+242E, "", e290ae), +_c(U+242F, "", e290af), +_c(U+2430, "", e290b0), +_c(U+2431, "", e290b1), +_c(U+2432, "", e290b2), +_c(U+2433, "", e290b3), +_c(U+2434, "", e290b4), +_c(U+2435, "", e290b5), +_c(U+2436, "", e290b6), +_c(U+2437, "", e290b7), +_c(U+2438, "", e290b8), +_c(U+2439, "", e290b9), +_c(U+243A, "", e290ba), +_c(U+243B, "", e290bb), +_c(U+243C, "", e290bc), +_c(U+243D, "", e290bd), +_c(U+243E, "", e290be), +_c(U+243F, "", e290bf), +_c(U+2440, "⑀", e29180), +_c(U+2441, "⑁", e29181), +_c(U+2442, "⑂", e29182), +_c(U+2443, "⑃", e29183), +_c(U+2444, "⑄", e29184), +_c(U+2445, "⑅", e29185), +_c(U+2446, "⑆", e29186), +_c(U+2447, "⑇", e29187), +_c(U+2448, "⑈", e29188), +_c(U+2449, "⑉", e29189), +_c(U+244A, "⑊", e2918a), +_c(U+244B, "", e2918b), +_c(U+244C, "", e2918c), +_c(U+244D, "", e2918d), +_c(U+244E, "", e2918e), +_c(U+244F, "", e2918f), +_c(U+2450, "", e29190), +_c(U+2451, "", e29191), +_c(U+2452, "", e29192), +_c(U+2453, "", e29193), +_c(U+2454, "", e29194), +_c(U+2455, "", e29195), +_c(U+2456, "", e29196), +_c(U+2457, "", e29197), +_c(U+2458, "", e29198), +_c(U+2459, "", e29199), +_c(U+245A, "", e2919a), +_c(U+245B, "", e2919b), +_c(U+245C, "", e2919c), +_c(U+245D, "", e2919d), +_c(U+245E, "", e2919e), +_c(U+245F, "", e2919f), +_c(U+2460, "①", e291a0), +_c(U+2461, "②", e291a1), +_c(U+2462, "③", e291a2), +_c(U+2463, "④", e291a3), +_c(U+2464, "⑤", e291a4), +_c(U+2465, "⑥", e291a5), +_c(U+2466, "⑦", e291a6), +_c(U+2467, "⑧", e291a7), +_c(U+2468, "⑨", e291a8), +_c(U+2469, "⑩", e291a9), +_c(U+246A, "⑪", e291aa), +_c(U+246B, "⑫", e291ab), +_c(U+246C, "⑬", e291ac), +_c(U+246D, "⑭", e291ad), +_c(U+246E, "⑮", e291ae), +_c(U+246F, "⑯", e291af), +_c(U+2470, "⑰", e291b0), +_c(U+2471, "⑱", e291b1), +_c(U+2472, "⑲", e291b2), +_c(U+2473, "⑳", e291b3), +_c(U+2474, "⑴", e291b4), +_c(U+2475, "⑵", e291b5), +_c(U+2476, "⑶", e291b6), +_c(U+2477, "⑷", e291b7), +_c(U+2478, "⑸", e291b8), +_c(U+2479, "⑹", e291b9), +_c(U+247A, "⑺", e291ba), +_c(U+247B, "⑻", e291bb), +_c(U+247C, "⑼", e291bc), +_c(U+247D, "⑽", e291bd), +_c(U+247E, "⑾", e291be), +_c(U+247F, "⑿", e291bf), +_c(U+2480, "⒀", e29280), +_c(U+2481, "⒁", e29281), +_c(U+2482, "⒂", e29282), +_c(U+2483, "⒃", e29283), +_c(U+2484, "⒄", e29284), +_c(U+2485, "⒅", e29285), +_c(U+2486, "⒆", e29286), +_c(U+2487, "⒇", e29287), +_c(U+2488, "⒈", e29288), +_c(U+2489, "⒉", e29289), +_c(U+248A, "⒊", e2928a), +_c(U+248B, "⒋", e2928b), +_c(U+248C, "⒌", e2928c), +_c(U+248D, "⒍", e2928d), +_c(U+248E, "⒎", e2928e), +_c(U+248F, "⒏", e2928f), +_c(U+2490, "⒐", e29290), +_c(U+2491, "⒑", e29291), +_c(U+2492, "⒒", e29292), +_c(U+2493, "⒓", e29293), +_c(U+2494, "⒔", e29294), +_c(U+2495, "⒕", e29295), +_c(U+2496, "⒖", e29296), +_c(U+2497, "⒗", e29297), +_c(U+2498, "⒘", e29298), +_c(U+2499, "⒙", e29299), +_c(U+249A, "⒚", e2929a), +_c(U+249B, "⒛", e2929b), +_c(U+249C, "⒜", e2929c), +_c(U+249D, "⒝", e2929d), +_c(U+249E, "⒞", e2929e), +_c(U+249F, "⒟", e2929f), +_c(U+2601, "☁", e29881), +_c(U+2602, "☂", e29882), +_c(U+2603, "☃", e29883), +_c(U+2604, "☄", e29884), +_c(U+2605, "★", e29885), +_c(U+2606, "☆", e29886), +_c(U+2607, "☇", e29887), +_c(U+2608, "☈", e29888), +_c(U+2609, "☉", e29889), +_c(U+260A, "☊", e2988a), +_c(U+260B, "☋", e2988b), +_c(U+260C, "☌", e2988c), +_c(U+260D, "☍", e2988d), +_c(U+260E, "☎", e2988e), +_c(U+260F, "☏", e2988f), +_c(U+2610, "☐", e29890), +_c(U+2611, "☑", e29891), +_c(U+2612, "☒", e29892), +_c(U+2613, "☓", e29893), +_c(U+2614, "☔", e29894), +_c(U+2615, "☕", e29895), +_c(U+2616, "☖", e29896), +_c(U+2617, "☗", e29897), +_c(U+2618, "☘", e29898), +_c(U+2619, "☙", e29899), +_c(U+261A, "☚", e2989a), +_c(U+261B, "☛", e2989b), +_c(U+261C, "☜", e2989c), +_c(U+261D, "☝", e2989d), +_c(U+261E, "☞", e2989e), +_c(U+261F, "☟", e2989f), +_c(U+2620, "☠", e298a0), +_c(U+2621, "☡", e298a1), +_c(U+2622, "☢", e298a2), +_c(U+2623, "☣", e298a3), +_c(U+2624, "☤", e298a4), +_c(U+2625, "☥", e298a5), +_c(U+2626, "☦", e298a6), +_c(U+2627, "☧", e298a7), +_c(U+2628, "☨", e298a8), +_c(U+2629, "☩", e298a9), +_c(U+262A, "☪", e298aa), +_c(U+262B, "☫", e298ab), +_c(U+262C, "☬", e298ac), +_c(U+262D, "☭", e298ad), +_c(U+262E, "☮", e298ae), +_c(U+262F, "☯", e298af), +_c(U+2630, "☰", e298b0), +_c(U+2631, "☱", e298b1), +_c(U+2632, "☲", e298b2), +_c(U+2633, "☳", e298b3), +_c(U+2634, "☴", e298b4), +_c(U+2635, "☵", e298b5), +_c(U+2636, "☶", e298b6), +_c(U+2637, "☷", e298b7), +_c(U+2638, "☸", e298b8), +_c(U+2639, "☹", e298b9), +_c(U+263A, "☺", e298ba), +_c(U+263B, "☻", e298bb), +_c(U+263C, "☼", e298bc), +_c(U+263D, "☽", e298bd), +_c(U+263E, "☾", e298be), +_c(U+263F, "☿", e298bf), +_c(U+2640, "♀", e29980), +_c(U+2641, "♁", e29981), +_c(U+2642, "♂", e29982), +_c(U+2643, "♃", e29983), +_c(U+2644, "♄", e29984), +_c(U+2645, "♅", e29985), +_c(U+2646, "♆", e29986), +_c(U+2647, "♇", e29987), +_c(U+2648, "♈", e29988), +_c(U+2649, "♉", e29989), +_c(U+264A, "♊", e2998a), +_c(U+264B, "♋", e2998b), +_c(U+264C, "♌", e2998c), +_c(U+264D, "♍", e2998d), +_c(U+264E, "♎", e2998e), +_c(U+264F, "♏", e2998f), +_c(U+2650, "♐", e29990), +_c(U+2651, "♑", e29991), +_c(U+2652, "♒", e29992), +_c(U+2653, "♓", e29993), +_c(U+2654, "♔", e29994), +_c(U+2655, "♕", e29995), +_c(U+2656, "♖", e29996), +_c(U+2657, "♗", e29997), +_c(U+2658, "♘", e29998), +_c(U+2659, "♙", e29999), +_c(U+265A, "♚", e2999a), +_c(U+265B, "♛", e2999b), +_c(U+265C, "♜", e2999c), +_c(U+265D, "♝", e2999d), +_c(U+265E, "♞", e2999e), +_c(U+265F, "♟", e2999f), +_c(U+2660, "♠", e299a0), +_c(U+2661, "♡", e299a1), +_c(U+2662, "♢", e299a2), +_c(U+2663, "♣", e299a3), +_c(U+2664, "♤", e299a4), +_c(U+2665, "♥", e299a5), +_c(U+2666, "♦", e299a6), +_c(U+2667, "♧", e299a7), +_c(U+2668, "♨", e299a8), +_c(U+2669, "♩", e299a9), +_c(U+266A, "♪", e299aa), +_c(U+266B, "♫", e299ab), +_c(U+266C, "♬", e299ac), +_c(U+266D, "♭", e299ad), +_c(U+266E, "♮", e299ae), +_c(U+266F, "♯", e299af), +_c(U+2670, "♰", e299b0), +_c(U+2671, "♱", e299b1), +_c(U+2672, "♲", e299b2), +_c(U+2673, "♳", e299b3), +_c(U+2674, "♴", e299b4), +_c(U+2675, "♵", e299b5), +_c(U+2676, "♶", e299b6), +_c(U+2677, "♷", e299b7), +_c(U+2678, "♸", e299b8), +_c(U+2679, "♹", e299b9), +_c(U+267A, "♺", e299ba), +_c(U+267B, "♻", e299bb), +_c(U+267C, "♼", e299bc), +_c(U+267D, "♽", e299bd), +_c(U+267E, "♾", e299be), +_c(U+267F, "♿", e299bf), +_c(U+2680, "⚀", e29a80), +_c(U+2681, "⚁", e29a81), +_c(U+2682, "⚂", e29a82), +_c(U+2683, "⚃", e29a83), +_c(U+2684, "⚄", e29a84), +_c(U+2685, "⚅", e29a85), +_c(U+2686, "⚆", e29a86), +_c(U+2687, "⚇", e29a87), +_c(U+2688, "⚈", e29a88), +_c(U+2689, "⚉", e29a89), +_c(U+268A, "⚊", e29a8a), +_c(U+268B, "⚋", e29a8b), +_c(U+268C, "⚌", e29a8c), +_c(U+268D, "⚍", e29a8d), +_c(U+268E, "⚎", e29a8e), +_c(U+268F, "⚏", e29a8f), +_c(U+2690, "⚐", e29a90), +_c(U+2691, "⚑", e29a91), +_c(U+2692, "⚒", e29a92), +_c(U+2693, "⚓", e29a93), +_c(U+2694, "⚔", e29a94), +_c(U+2695, "⚕", e29a95), +_c(U+2696, "⚖", e29a96), +_c(U+2697, "⚗", e29a97), +_c(U+2698, "⚘", e29a98), +_c(U+2699, "⚙", e29a99), +_c(U+269A, "⚚", e29a9a), +_c(U+269B, "⚛", e29a9b), +_c(U+269C, "⚜", e29a9c), +_c(U+269D, "⚝", e29a9d), +_c(U+269E, "⚞", e29a9e), +_c(U+269F, "⚟", e29a9f), +_c(U+26A0, "⚠", e29aa0), +_c(U+26A1, "⚡", e29aa1), +_c(U+26A2, "⚢", e29aa2), +_c(U+26A3, "⚣", e29aa3), +_c(U+26A4, "⚤", e29aa4), +_c(U+26A5, "⚥", e29aa5), +_c(U+26A6, "⚦", e29aa6), +_c(U+26A7, "⚧", e29aa7), +_c(U+26A8, "⚨", e29aa8), +_c(U+26A9, "⚩", e29aa9), +_c(U+26AA, "⚪", e29aaa), +_c(U+26AB, "⚫", e29aab), +_c(U+26AC, "⚬", e29aac), +_c(U+26AD, "⚭", e29aad), +_c(U+26AE, "⚮", e29aae), +_c(U+26AF, "⚯", e29aaf), +_c(U+26B0, "⚰", e29ab0), +_c(U+26B1, "⚱", e29ab1), +_c(U+26B2, "⚲", e29ab2), +_c(U+26B3, "⚳", e29ab3), +_c(U+26B4, "⚴", e29ab4), +_c(U+26B5, "⚵", e29ab5), +_c(U+26B6, "⚶", e29ab6), +_c(U+26B7, "⚷", e29ab7), +_c(U+26B8, "⚸", e29ab8), +_c(U+26B9, "⚹", e29ab9), +_c(U+26BA, "⚺", e29aba), +_c(U+26BB, "⚻", e29abb), +_c(U+26BC, "⚼", e29abc), +_c(U+26BD, "⚽", e29abd), +_c(U+26BE, "⚾", e29abe), +_c(U+26BF, "⚿", e29abf), +_c(U+26C0, "⛀", e29b80), +_c(U+26C1, "⛁", e29b81), +_c(U+26C2, "⛂", e29b82), +_c(U+26C3, "⛃", e29b83), +_c(U+26C4, "⛄", e29b84), +_c(U+26C5, "⛅", e29b85), +_c(U+26C6, "⛆", e29b86), +_c(U+26C7, "⛇", e29b87), +_c(U+26C8, "⛈", e29b88), +_c(U+26C9, "⛉", e29b89), +_c(U+26CA, "⛊", e29b8a), +_c(U+26CB, "⛋", e29b8b), +_c(U+26CC, "⛌", e29b8c), +_c(U+26CD, "⛍", e29b8d), +_c(U+26CE, "⛎", e29b8e), +_c(U+26CF, "⛏", e29b8f), +_c(U+26D0, "⛐", e29b90), +_c(U+26D1, "⛑", e29b91), +_c(U+26D2, "⛒", e29b92), +_c(U+26D3, "⛓", e29b93), +_c(U+26D4, "⛔", e29b94), +_c(U+26D5, "⛕", e29b95), +_c(U+26D6, "⛖", e29b96), +_c(U+26D7, "⛗", e29b97), +_c(U+26D8, "⛘", e29b98), +_c(U+26D9, "⛙", e29b99), +_c(U+26DA, "⛚", e29b9a), +_c(U+26DB, "⛛", e29b9b), +_c(U+26DC, "⛜", e29b9c), +_c(U+26DD, "⛝", e29b9d), +_c(U+26DE, "⛞", e29b9e), +_c(U+26DF, "⛟", e29b9f), +_c(U+26E0, "⛠", e29ba0), +_c(U+26E1, "⛡", e29ba1), +_c(U+26E2, "⛢", e29ba2), +_c(U+26E3, "⛣", e29ba3), +_c(U+26E4, "⛤", e29ba4), +_c(U+26E5, "⛥", e29ba5), +_c(U+26E6, "⛦", e29ba6), +_c(U+26E7, "⛧", e29ba7), +_c(U+26E8, "⛨", e29ba8), +_c(U+26E9, "⛩", e29ba9), +_c(U+26EA, "⛪", e29baa), +_c(U+26EB, "⛫", e29bab), +_c(U+26EC, "⛬", e29bac), +_c(U+26ED, "⛭", e29bad), +_c(U+26EE, "⛮", e29bae), +_c(U+26EF, "⛯", e29baf), +_c(U+26F0, "⛰", e29bb0), +_c(U+26F1, "⛱", e29bb1), +_c(U+26F2, "⛲", e29bb2), +_c(U+26F3, "⛳", e29bb3), +_c(U+26F4, "⛴", e29bb4), +_c(U+26F5, "⛵", e29bb5), +_c(U+26F6, "⛶", e29bb6), +_c(U+26F7, "⛷", e29bb7), +_c(U+26F8, "⛸", e29bb8), +_c(U+26F9, "⛹", e29bb9), +_c(U+26FA, "⛺", e29bba), +_c(U+26FB, "⛻", e29bbb), +_c(U+26FC, "⛼", e29bbc), +_c(U+26FD, "⛽", e29bbd), +_c(U+26FE, "⛾", e29bbe), +_c(U+26FF, "⛿", e29bbf), +_c(U+2700, "✀", e29c80), +_c(U+2701, "✁", e29c81), +_c(U+2702, "✂", e29c82), +_c(U+2703, "✃", e29c83), +_c(U+2704, "✄", e29c84), +_c(U+2705, "✅", e29c85), +_c(U+2706, "✆", e29c86), +_c(U+2707, "✇", e29c87), +_c(U+2708, "✈", e29c88), +_c(U+2709, "✉", e29c89), +_c(U+270A, "✊", e29c8a), +_c(U+270B, "✋", e29c8b), +_c(U+270C, "✌", e29c8c), +_c(U+270D, "✍", e29c8d), +_c(U+270E, "✎", e29c8e), +_c(U+270F, "✏", e29c8f), +_c(U+2710, "✐", e29c90), +_c(U+2711, "✑", e29c91), +_c(U+2712, "✒", e29c92), +_c(U+2713, "✓", e29c93), +_c(U+2714, "✔", e29c94), +_c(U+2715, "✕", e29c95), +_c(U+2716, "✖", e29c96), +_c(U+2717, "✗", e29c97), +_c(U+2718, "✘", e29c98), +_c(U+2719, "✙", e29c99), +_c(U+271A, "✚", e29c9a), +_c(U+271B, "✛", e29c9b), +_c(U+271C, "✜", e29c9c), +_c(U+271D, "✝", e29c9d), +_c(U+271E, "✞", e29c9e), +_c(U+271F, "✟", e29c9f), +_c(U+2720, "✠", e29ca0), +_c(U+2721, "✡", e29ca1), +_c(U+2722, "✢", e29ca2), +_c(U+2723, "✣", e29ca3), +_c(U+2724, "✤", e29ca4), +_c(U+2725, "✥", e29ca5), +_c(U+2726, "✦", e29ca6), +_c(U+2727, "✧", e29ca7), +_c(U+2728, "✨", e29ca8), +_c(U+2729, "✩", e29ca9), +_c(U+272A, "✪", e29caa), +_c(U+272B, "✫", e29cab), +_c(U+272C, "✬", e29cac), +_c(U+272D, "✭", e29cad), +_c(U+272E, "✮", e29cae), +_c(U+272F, "✯", e29caf), +_c(U+2730, "✰", e29cb0), +_c(U+2731, "✱", e29cb1), +_c(U+2732, "✲", e29cb2), +_c(U+2733, "✳", e29cb3), +_c(U+2734, "✴", e29cb4), +_c(U+2735, "✵", e29cb5), +_c(U+2736, "✶", e29cb6), +_c(U+2737, "✷", e29cb7), +_c(U+2738, "✸", e29cb8), +_c(U+2739, "✹", e29cb9), +_c(U+273A, "✺", e29cba), +_c(U+273B, "✻", e29cbb), +_c(U+273C, "✼", e29cbc), +_c(U+273D, "✽", e29cbd), +_c(U+273E, "✾", e29cbe), +_c(U+273F, "✿", e29cbf), +_c(U+2740, "❀", e29d80), +_c(U+2741, "❁", e29d81), +_c(U+2742, "❂", e29d82), +_c(U+2743, "❃", e29d83), +_c(U+2744, "❄", e29d84), +_c(U+2745, "❅", e29d85), +_c(U+2746, "❆", e29d86), +_c(U+2747, "❇", e29d87), +_c(U+2748, "❈", e29d88), +_c(U+2749, "❉", e29d89), +_c(U+274A, "❊", e29d8a), +_c(U+274B, "❋", e29d8b), +_c(U+274C, "❌", e29d8c), +_c(U+274D, "❍", e29d8d), +_c(U+274E, "❎", e29d8e), +_c(U+274F, "❏", e29d8f), +_c(U+2750, "❐", e29d90), +_c(U+2751, "❑", e29d91), +_c(U+2752, "❒", e29d92), +_c(U+2753, "❓", e29d93), +_c(U+2754, "❔", e29d94), +_c(U+2755, "❕", e29d95), +_c(U+2756, "❖", e29d96), +_c(U+2757, "❗", e29d97), +_c(U+2758, "❘", e29d98), +_c(U+2759, "❙", e29d99), +_c(U+275A, "❚", e29d9a), +_c(U+275B, "❛", e29d9b), +_c(U+275C, "❜", e29d9c), +_c(U+275D, "❝", e29d9d), +_c(U+275E, "❞", e29d9e), +_c(U+275F, "❟", e29d9f), +_c(U+2760, "❠", e29da0), +_c(U+2761, "❡", e29da1), +_c(U+2762, "❢", e29da2), +_c(U+2763, "❣", e29da3), +_c(U+2764, "❤", e29da4), +_c(U+2765, "❥", e29da5), +_c(U+2766, "❦", e29da6), +_c(U+2767, "❧", e29da7), +_c(U+2768, "❨", e29da8), +_c(U+2769, "❩", e29da9), +_c(U+276A, "❪", e29daa), +_c(U+276B, "❫", e29dab), +_c(U+276C, "❬", e29dac), +_c(U+276D, "❭", e29dad), +_c(U+276E, "❮", e29dae), +_c(U+276F, "❯", e29daf), +_c(U+2770, "❰", e29db0), +_c(U+2771, "❱", e29db1), +_c(U+2772, "❲", e29db2), +_c(U+2773, "❳", e29db3), +_c(U+2774, "❴", e29db4), +_c(U+2775, "❵", e29db5), +_c(U+2776, "❶", e29db6), +_c(U+2777, "❷", e29db7), +_c(U+2778, "❸", e29db8), +_c(U+2779, "❹", e29db9), +_c(U+277A, "❺", e29dba), +_c(U+277B, "❻", e29dbb), +_c(U+277C, "❼", e29dbc), +_c(U+277D, "❽", e29dbd), +_c(U+277E, "❾", e29dbe), +_c(U+277F, "❿", e29dbf), +_c(U+2780, "➀", e29e80), +_c(U+2781, "➁", e29e81), +_c(U+2782, "➂", e29e82), +_c(U+2783, "➃", e29e83), +_c(U+2784, "➄", e29e84), +_c(U+2785, "➅", e29e85), +_c(U+2786, "➆", e29e86), +_c(U+2787, "➇", e29e87), +_c(U+2788, "➈", e29e88), +_c(U+2789, "➉", e29e89), +_c(U+278A, "➊", e29e8a), +_c(U+278B, "➋", e29e8b), +_c(U+278C, "➌", e29e8c), +_c(U+278D, "➍", e29e8d), +_c(U+278E, "➎", e29e8e), +_c(U+278F, "➏", e29e8f), +_c(U+2790, "➐", e29e90), +_c(U+2791, "➑", e29e91), +_c(U+2792, "➒", e29e92), +_c(U+2793, "➓", e29e93), +_c(U+2794, "➔", e29e94), +_c(U+2795, "➕", e29e95), +_c(U+2796, "➖", e29e96), +_c(U+2797, "➗", e29e97), +_c(U+2798, "➘", e29e98), +_c(U+2799, "➙", e29e99), +_c(U+279A, "➚", e29e9a), +_c(U+279B, "➛", e29e9b), +_c(U+279C, "➜", e29e9c), +_c(U+279D, "➝", e29e9d), +_c(U+279E, "➞", e29e9e), +_c(U+279F, "➟", e29e9f), +_c(U+27A0, "➠", e29ea0), +_c(U+27A1, "➡", e29ea1), +_c(U+27A2, "➢", e29ea2), +_c(U+27A3, "➣", e29ea3), +_c(U+27A4, "➤", e29ea4), +_c(U+27A5, "➥", e29ea5), +_c(U+27A6, "➦", e29ea6), +_c(U+27A7, "➧", e29ea7), +_c(U+27A8, "➨", e29ea8), +_c(U+27A9, "➩", e29ea9), +_c(U+27AA, "➪", e29eaa), +_c(U+27AB, "➫", e29eab), +_c(U+27AC, "➬", e29eac), +_c(U+27AD, "➭", e29ead), +_c(U+27AE, "➮", e29eae), +_c(U+27AF, "➯", e29eaf), +_c(U+27B0, "➰", e29eb0), +_c(U+27B1, "➱", e29eb1), +_c(U+27B2, "➲", e29eb2), +_c(U+27B3, "➳", e29eb3), +_c(U+27B4, "➴", e29eb4), +_c(U+27B5, "➵", e29eb5), +_c(U+27B6, "➶", e29eb6), +_c(U+27B7, "➷", e29eb7), +_c(U+27B8, "➸", e29eb8), +_c(U+27B9, "➹", e29eb9), +_c(U+27BA, "➺", e29eba), +_c(U+27BB, "➻", e29ebb), +_c(U+27BC, "➼", e29ebc), +_c(U+27BD, "➽", e29ebd), +_c(U+27BE, "➾", e29ebe), +_c(U+27BF, "➿", e29ebf), +_c(U+27C0, "⟀", e29f80), +_c(U+27C1, "⟁", e29f81), +_c(U+27C2, "⟂", e29f82), +_c(U+27C3, "⟃", e29f83), +_c(U+27C4, "⟄", e29f84), +_c(U+27C5, "⟅", e29f85), +_c(U+27C6, "⟆", e29f86), +_c(U+27C7, "⟇", e29f87), +_c(U+27C8, "⟈", e29f88), +_c(U+27C9, "⟉", e29f89), +_c(U+27CA, "⟊", e29f8a), +_c(U+27CB, "⟋", e29f8b), +_c(U+27CC, "⟌", e29f8c), +_c(U+27CD, "⟍", e29f8d), +_c(U+27CE, "⟎", e29f8e), +_c(U+27CF, "⟏", e29f8f), +_c(U+27D0, "⟐", e29f90), +_c(U+27D1, "⟑", e29f91), +_c(U+27D2, "⟒", e29f92), +_c(U+27D3, "⟓", e29f93), +_c(U+27D4, "⟔", e29f94), +_c(U+27D5, "⟕", e29f95), +_c(U+27D6, "⟖", e29f96), +_c(U+27D7, "⟗", e29f97), +_c(U+27D8, "⟘", e29f98), +_c(U+27D9, "⟙", e29f99), +_c(U+27DA, "⟚", e29f9a), +_c(U+27DB, "⟛", e29f9b), +_c(U+27DC, "⟜", e29f9c), +_c(U+27DD, "⟝", e29f9d), +_c(U+27DE, "⟞", e29f9e), +_c(U+27DF, "⟟", e29f9f), +_c(U+27E0, "⟠", e29fa0), +_c(U+27E1, "⟡", e29fa1), +_c(U+27E2, "⟢", e29fa2), +_c(U+27E3, "⟣", e29fa3), +_c(U+27E4, "⟤", e29fa4), +_c(U+27E5, "⟥", e29fa5), +_c(U+27E6, "⟦", e29fa6), +_c(U+27E7, "⟧", e29fa7), +_c(U+27E8, "⟨", e29fa8), +_c(U+27E9, "⟩", e29fa9), +_c(U+27EA, "⟪", e29faa), +_c(U+27EB, "⟫", e29fab), +_c(U+27EC, "⟬", e29fac), +_c(U+27ED, "⟭", e29fad), +_c(U+27EE, "⟮", e29fae), +_c(U+27EF, "⟯", e29faf), +_c(U+27F0, "⟰", e29fb0), +_c(U+27F1, "⟱", e29fb1), +_c(U+27F2, "⟲", e29fb2), +_c(U+27F3, "⟳", e29fb3), +_c(U+27F4, "⟴", e29fb4), +_c(U+27F5, "⟵", e29fb5), +_c(U+27F6, "⟶", e29fb6), +_c(U+27F7, "⟷", e29fb7), +_c(U+27F8, "⟸", e29fb8), +_c(U+27F9, "⟹", e29fb9), +_c(U+27FA, "⟺", e29fba), +_c(U+27FB, "⟻", e29fbb), +_c(U+27FC, "⟼", e29fbc), +_c(U+27FD, "⟽", e29fbd), +_c(U+27FE, "⟾", e29fbe), +_c(U+27FF, "⟿", e29fbf), +_c(U+2800, "⠀", e2a080), +_c(U+2801, "⠁", e2a081), +_c(U+2802, "⠂", e2a082), +_c(U+2803, "⠃", e2a083), +_c(U+2804, "⠄", e2a084), +_c(U+2805, "⠅", e2a085), +_c(U+2806, "⠆", e2a086), +_c(U+2807, "⠇", e2a087), +_c(U+2808, "⠈", e2a088), +_c(U+2809, "⠉", e2a089), +_c(U+280A, "⠊", e2a08a), +_c(U+280B, "⠋", e2a08b), +_c(U+280C, "⠌", e2a08c), +_c(U+280D, "⠍", e2a08d), +_c(U+280E, "⠎", e2a08e), +_c(U+280F, "⠏", e2a08f), +_c(U+2810, "⠐", e2a090), +_c(U+2811, "⠑", e2a091), +_c(U+2812, "⠒", e2a092), +_c(U+2813, "⠓", e2a093), +_c(U+2814, "⠔", e2a094), +_c(U+2815, "⠕", e2a095), +_c(U+2816, "⠖", e2a096), +_c(U+2817, "⠗", e2a097), +_c(U+2818, "⠘", e2a098), +_c(U+2819, "⠙", e2a099), +_c(U+281A, "⠚", e2a09a), +_c(U+281B, "⠛", e2a09b), +_c(U+281C, "⠜", e2a09c), +_c(U+281D, "⠝", e2a09d), +_c(U+281E, "⠞", e2a09e), +_c(U+281F, "⠟", e2a09f), +_c(U+2820, "⠠", e2a0a0), +_c(U+2821, "⠡", e2a0a1), +_c(U+2822, "⠢", e2a0a2), +_c(U+2823, "⠣", e2a0a3), +_c(U+2824, "⠤", e2a0a4), +_c(U+2825, "⠥", e2a0a5), +_c(U+2826, "⠦", e2a0a6), +_c(U+2827, "⠧", e2a0a7), +_c(U+2828, "⠨", e2a0a8), +_c(U+2829, "⠩", e2a0a9), +_c(U+282A, "⠪", e2a0aa), +_c(U+282B, "⠫", e2a0ab), +_c(U+282C, "⠬", e2a0ac), +_c(U+282D, "⠭", e2a0ad), +_c(U+282E, "⠮", e2a0ae), +_c(U+282F, "⠯", e2a0af), +_c(U+2830, "⠰", e2a0b0), +_c(U+2831, "⠱", e2a0b1), +_c(U+2832, "⠲", e2a0b2), +_c(U+2833, "⠳", e2a0b3), +_c(U+2834, "⠴", e2a0b4), +_c(U+2835, "⠵", e2a0b5), +_c(U+2836, "⠶", e2a0b6), +_c(U+2837, "⠷", e2a0b7), +_c(U+2838, "⠸", e2a0b8), +_c(U+2839, "⠹", e2a0b9), +_c(U+283A, "⠺", e2a0ba), +_c(U+283B, "⠻", e2a0bb), +_c(U+283C, "⠼", e2a0bc), +_c(U+283D, "⠽", e2a0bd), +_c(U+283E, "⠾", e2a0be), +_c(U+283F, "⠿", e2a0bf), +_c(U+2840, "⡀", e2a180), +_c(U+2841, "⡁", e2a181), +_c(U+2842, "⡂", e2a182), +_c(U+2843, "⡃", e2a183), +_c(U+2844, "⡄", e2a184), +_c(U+2845, "⡅", e2a185), +_c(U+2846, "⡆", e2a186), +_c(U+2847, "⡇", e2a187), +_c(U+2848, "⡈", e2a188), +_c(U+2849, "⡉", e2a189), +_c(U+284A, "⡊", e2a18a), +_c(U+284B, "⡋", e2a18b), +_c(U+284C, "⡌", e2a18c), +_c(U+284D, "⡍", e2a18d), +_c(U+284E, "⡎", e2a18e), +_c(U+284F, "⡏", e2a18f), +_c(U+2850, "⡐", e2a190), +_c(U+2851, "⡑", e2a191), +_c(U+2852, "⡒", e2a192), +_c(U+2853, "⡓", e2a193), +_c(U+2854, "⡔", e2a194), +_c(U+2855, "⡕", e2a195), +_c(U+2856, "⡖", e2a196), +_c(U+2857, "⡗", e2a197), +_c(U+2858, "⡘", e2a198), +_c(U+2859, "⡙", e2a199), +_c(U+285A, "⡚", e2a19a), +_c(U+285B, "⡛", e2a19b), +_c(U+285C, "⡜", e2a19c), +_c(U+285D, "⡝", e2a19d), +_c(U+285E, "⡞", e2a19e), +_c(U+285F, "⡟", e2a19f), +_c(U+2860, "⡠", e2a1a0), +_c(U+2861, "⡡", e2a1a1), +_c(U+2862, "⡢", e2a1a2), +_c(U+2863, "⡣", e2a1a3), +_c(U+2864, "⡤", e2a1a4), +_c(U+2865, "⡥", e2a1a5), +_c(U+2866, "⡦", e2a1a6), +_c(U+2867, "⡧", e2a1a7), +_c(U+2868, "⡨", e2a1a8), +_c(U+2869, "⡩", e2a1a9), +_c(U+286A, "⡪", e2a1aa), +_c(U+286B, "⡫", e2a1ab), +_c(U+286C, "⡬", e2a1ac), +_c(U+286D, "⡭", e2a1ad), +_c(U+286E, "⡮", e2a1ae), +_c(U+286F, "⡯", e2a1af), +_c(U+2870, "⡰", e2a1b0), +_c(U+2871, "⡱", e2a1b1), +_c(U+2872, "⡲", e2a1b2), +_c(U+2873, "⡳", e2a1b3), +_c(U+2874, "⡴", e2a1b4), +_c(U+2875, "⡵", e2a1b5), +_c(U+2876, "⡶", e2a1b6), +_c(U+2877, "⡷", e2a1b7), +_c(U+2878, "⡸", e2a1b8), +_c(U+2879, "⡹", e2a1b9), +_c(U+287A, "⡺", e2a1ba), +_c(U+287B, "⡻", e2a1bb), +_c(U+287C, "⡼", e2a1bc), +_c(U+287D, "⡽", e2a1bd), +_c(U+287E, "⡾", e2a1be), +_c(U+287F, "⡿", e2a1bf), +_c(U+2880, "⢀", e2a280), +_c(U+2881, "⢁", e2a281), +_c(U+2882, "⢂", e2a282), +_c(U+2883, "⢃", e2a283), +_c(U+2884, "⢄", e2a284), +_c(U+2885, "⢅", e2a285), +_c(U+2886, "⢆", e2a286), +_c(U+2887, "⢇", e2a287), +_c(U+2888, "⢈", e2a288), +_c(U+2889, "⢉", e2a289), +_c(U+288A, "⢊", e2a28a), +_c(U+288B, "⢋", e2a28b), +_c(U+288C, "⢌", e2a28c), +_c(U+288D, "⢍", e2a28d), +_c(U+288E, "⢎", e2a28e), +_c(U+288F, "⢏", e2a28f), +_c(U+2890, "⢐", e2a290), +_c(U+2891, "⢑", e2a291), +_c(U+2892, "⢒", e2a292), +_c(U+2893, "⢓", e2a293), +_c(U+2894, "⢔", e2a294), +_c(U+2895, "⢕", e2a295), +_c(U+2896, "⢖", e2a296), +_c(U+2897, "⢗", e2a297), +_c(U+2898, "⢘", e2a298), +_c(U+2899, "⢙", e2a299), +_c(U+289A, "⢚", e2a29a), +_c(U+289B, "⢛", e2a29b), +_c(U+289C, "⢜", e2a29c), +_c(U+289D, "⢝", e2a29d), +_c(U+289E, "⢞", e2a29e), +_c(U+289F, "⢟", e2a29f), +_c(U+28A0, "⢠", e2a2a0), +_c(U+28A1, "⢡", e2a2a1), +_c(U+28A2, "⢢", e2a2a2), +_c(U+28A3, "⢣", e2a2a3), +_c(U+28A4, "⢤", e2a2a4), +_c(U+28A5, "⢥", e2a2a5), +_c(U+28A6, "⢦", e2a2a6), +_c(U+28A7, "⢧", e2a2a7), +_c(U+28A8, "⢨", e2a2a8), +_c(U+28A9, "⢩", e2a2a9), +_c(U+28AA, "⢪", e2a2aa), +_c(U+28AB, "⢫", e2a2ab), +_c(U+28AC, "⢬", e2a2ac), +_c(U+28AD, "⢭", e2a2ad), +_c(U+28AE, "⢮", e2a2ae), +_c(U+28AF, "⢯", e2a2af), +_c(U+28B0, "⢰", e2a2b0), +_c(U+28B1, "⢱", e2a2b1), +_c(U+28B2, "⢲", e2a2b2), +_c(U+28B3, "⢳", e2a2b3), +_c(U+28B4, "⢴", e2a2b4), +_c(U+28B5, "⢵", e2a2b5), +_c(U+28B6, "⢶", e2a2b6), +_c(U+28B7, "⢷", e2a2b7), +_c(U+28B8, "⢸", e2a2b8), +_c(U+28B9, "⢹", e2a2b9), +_c(U+28BA, "⢺", e2a2ba), +_c(U+28BB, "⢻", e2a2bb), +_c(U+28BC, "⢼", e2a2bc), +_c(U+28BD, "⢽", e2a2bd), +_c(U+28BE, "⢾", e2a2be), +_c(U+28BF, "⢿", e2a2bf), +_c(U+28C0, "⣀", e2a380), +_c(U+28C1, "⣁", e2a381), +_c(U+28C2, "⣂", e2a382), +_c(U+28C3, "⣃", e2a383), +_c(U+28C4, "⣄", e2a384), +_c(U+28C5, "⣅", e2a385), +_c(U+28C6, "⣆", e2a386), +_c(U+28C7, "⣇", e2a387), +_c(U+28C8, "⣈", e2a388), +_c(U+28C9, "⣉", e2a389), +_c(U+28CA, "⣊", e2a38a), +_c(U+28CB, "⣋", e2a38b), +_c(U+28CC, "⣌", e2a38c), +_c(U+28CD, "⣍", e2a38d), +_c(U+28CE, "⣎", e2a38e), +_c(U+28CF, "⣏", e2a38f), +_c(U+28D0, "⣐", e2a390), +_c(U+28D1, "⣑", e2a391), +_c(U+28D2, "⣒", e2a392), +_c(U+28D3, "⣓", e2a393), +_c(U+28D4, "⣔", e2a394), +_c(U+28D5, "⣕", e2a395), +_c(U+28D6, "⣖", e2a396), +_c(U+28D7, "⣗", e2a397), +_c(U+28D8, "⣘", e2a398), +_c(U+28D9, "⣙", e2a399), +_c(U+28DA, "⣚", e2a39a), +_c(U+28DB, "⣛", e2a39b), +_c(U+28DC, "⣜", e2a39c), +_c(U+28DD, "⣝", e2a39d), +_c(U+28DE, "⣞", e2a39e), +_c(U+28DF, "⣟", e2a39f), +_c(U+28E0, "⣠", e2a3a0), +_c(U+28E1, "⣡", e2a3a1), +_c(U+28E2, "⣢", e2a3a2), +_c(U+28E3, "⣣", e2a3a3), +_c(U+28E4, "⣤", e2a3a4), +_c(U+28E5, "⣥", e2a3a5), +_c(U+28E6, "⣦", e2a3a6), +_c(U+28E7, "⣧", e2a3a7), +_c(U+28E8, "⣨", e2a3a8), +_c(U+28E9, "⣩", e2a3a9), +_c(U+28EA, "⣪", e2a3aa), +_c(U+28EB, "⣫", e2a3ab), +_c(U+28EC, "⣬", e2a3ac), +_c(U+28ED, "⣭", e2a3ad), +_c(U+28EE, "⣮", e2a3ae), +_c(U+28EF, "⣯", e2a3af), +_c(U+28F0, "⣰", e2a3b0), +_c(U+28F1, "⣱", e2a3b1), +_c(U+28F2, "⣲", e2a3b2), +_c(U+28F3, "⣳", e2a3b3), +_c(U+28F4, "⣴", e2a3b4), +_c(U+28F5, "⣵", e2a3b5), +_c(U+28F6, "⣶", e2a3b6), +_c(U+28F7, "⣷", e2a3b7), +_c(U+28F8, "⣸", e2a3b8), +_c(U+28F9, "⣹", e2a3b9), +_c(U+28FA, "⣺", e2a3ba), +_c(U+28FB, "⣻", e2a3bb), +_c(U+28FC, "⣼", e2a3bc), +_c(U+28FD, "⣽", e2a3bd), +_c(U+28FE, "⣾", e2a3be), +_c(U+28FF, "⣿", e2a3bf), +_c(U+2900, "⤀", e2a480), +_c(U+2901, "⤁", e2a481), +_c(U+2902, "⤂", e2a482), +_c(U+2903, "⤃", e2a483), +_c(U+2904, "⤄", e2a484), +_c(U+2905, "⤅", e2a485), +_c(U+2906, "⤆", e2a486), +_c(U+2907, "⤇", e2a487), +_c(U+2908, "⤈", e2a488), +_c(U+2909, "⤉", e2a489), +_c(U+290A, "⤊", e2a48a), +_c(U+290B, "⤋", e2a48b), +_c(U+290C, "⤌", e2a48c), +_c(U+290D, "⤍", e2a48d), +_c(U+290E, "⤎", e2a48e), +_c(U+290F, "⤏", e2a48f), +_c(U+2910, "⤐", e2a490), +_c(U+2911, "⤑", e2a491), +_c(U+2912, "⤒", e2a492), +_c(U+2913, "⤓", e2a493), +_c(U+2914, "⤔", e2a494), +_c(U+2915, "⤕", e2a495), +_c(U+2916, "⤖", e2a496), +_c(U+2917, "⤗", e2a497), +_c(U+2918, "⤘", e2a498), +_c(U+2919, "⤙", e2a499), +_c(U+291A, "⤚", e2a49a), +_c(U+291B, "⤛", e2a49b), +_c(U+291C, "⤜", e2a49c), +_c(U+291D, "⤝", e2a49d), +_c(U+291E, "⤞", e2a49e), +_c(U+291F, "⤟", e2a49f), +_c(U+2920, "⤠", e2a4a0), +_c(U+2921, "⤡", e2a4a1), +_c(U+2922, "⤢", e2a4a2), +_c(U+2923, "⤣", e2a4a3), +_c(U+2924, "⤤", e2a4a4), +_c(U+2925, "⤥", e2a4a5), +_c(U+2926, "⤦", e2a4a6), +_c(U+2927, "⤧", e2a4a7), +_c(U+2928, "⤨", e2a4a8), +_c(U+2929, "⤩", e2a4a9), +_c(U+292A, "⤪", e2a4aa), +_c(U+292B, "⤫", e2a4ab), +_c(U+292C, "⤬", e2a4ac), +_c(U+292D, "⤭", e2a4ad), +_c(U+292E, "⤮", e2a4ae), +_c(U+292F, "⤯", e2a4af), +_c(U+2930, "⤰", e2a4b0), +_c(U+2931, "⤱", e2a4b1), +_c(U+2932, "⤲", e2a4b2), +_c(U+2933, "⤳", e2a4b3), +_c(U+2934, "⤴", e2a4b4), +_c(U+2935, "⤵", e2a4b5), +_c(U+2936, "⤶", e2a4b6), +_c(U+2937, "⤷", e2a4b7), +_c(U+2938, "⤸", e2a4b8), +_c(U+2939, "⤹", e2a4b9), +_c(U+293A, "⤺", e2a4ba), +_c(U+293B, "⤻", e2a4bb), +_c(U+293C, "⤼", e2a4bc), +_c(U+293D, "⤽", e2a4bd), +_c(U+293E, "⤾", e2a4be), +_c(U+293F, "⤿", e2a4bf), +_c(U+2940, "⥀", e2a580), +_c(U+2941, "⥁", e2a581), +_c(U+2942, "⥂", e2a582), +_c(U+2943, "⥃", e2a583), +_c(U+2944, "⥄", e2a584), +_c(U+2945, "⥅", e2a585), +_c(U+2946, "⥆", e2a586), +_c(U+2947, "⥇", e2a587), +_c(U+2948, "⥈", e2a588), +_c(U+2949, "⥉", e2a589), +_c(U+294A, "⥊", e2a58a), +_c(U+294B, "⥋", e2a58b), +_c(U+294C, "⥌", e2a58c), +_c(U+294D, "⥍", e2a58d), +_c(U+294E, "⥎", e2a58e), +_c(U+294F, "⥏", e2a58f), +_c(U+2950, "⥐", e2a590), +_c(U+2951, "⥑", e2a591), +_c(U+2952, "⥒", e2a592), +_c(U+2953, "⥓", e2a593), +_c(U+2954, "⥔", e2a594), +_c(U+2955, "⥕", e2a595), +_c(U+2956, "⥖", e2a596), +_c(U+2957, "⥗", e2a597), +_c(U+2958, "⥘", e2a598), +_c(U+2959, "⥙", e2a599), +_c(U+295A, "⥚", e2a59a), +_c(U+295B, "⥛", e2a59b), +_c(U+295C, "⥜", e2a59c), +_c(U+295D, "⥝", e2a59d), +_c(U+295E, "⥞", e2a59e), +_c(U+295F, "⥟", e2a59f), +_c(U+2960, "⥠", e2a5a0), +_c(U+2961, "⥡", e2a5a1), +_c(U+2962, "⥢", e2a5a2), +_c(U+2963, "⥣", e2a5a3), +_c(U+2964, "⥤", e2a5a4), +_c(U+2965, "⥥", e2a5a5), +_c(U+2966, "⥦", e2a5a6), +_c(U+2967, "⥧", e2a5a7), +_c(U+2968, "⥨", e2a5a8), +_c(U+2969, "⥩", e2a5a9), +_c(U+296A, "⥪", e2a5aa), +_c(U+296B, "⥫", e2a5ab), +_c(U+296C, "⥬", e2a5ac), +_c(U+296D, "⥭", e2a5ad), +_c(U+296E, "⥮", e2a5ae), +_c(U+296F, "⥯", e2a5af), +_c(U+2970, "⥰", e2a5b0), +_c(U+2971, "⥱", e2a5b1), +_c(U+2972, "⥲", e2a5b2), +_c(U+2973, "⥳", e2a5b3), +_c(U+2974, "⥴", e2a5b4), +_c(U+2975, "⥵", e2a5b5), +_c(U+2976, "⥶", e2a5b6), +_c(U+2977, "⥷", e2a5b7), +_c(U+2978, "⥸", e2a5b8), +_c(U+2979, "⥹", e2a5b9), +_c(U+297A, "⥺", e2a5ba), +_c(U+297B, "⥻", e2a5bb), +_c(U+297C, "⥼", e2a5bc), +_c(U+297D, "⥽", e2a5bd), +_c(U+297E, "⥾", e2a5be), +_c(U+297F, "⥿", e2a5bf), +_c(U+2980, "⦀", e2a680), +_c(U+2981, "⦁", e2a681), +_c(U+2982, "⦂", e2a682), +_c(U+2983, "⦃", e2a683), +_c(U+2984, "⦄", e2a684), +_c(U+2985, "⦅", e2a685), +_c(U+2986, "⦆", e2a686), +_c(U+2987, "⦇", e2a687), +_c(U+2988, "⦈", e2a688), +_c(U+2989, "⦉", e2a689), +_c(U+298A, "⦊", e2a68a), +_c(U+298B, "⦋", e2a68b), +_c(U+298C, "⦌", e2a68c), +_c(U+298D, "⦍", e2a68d), +_c(U+298E, "⦎", e2a68e), +_c(U+298F, "⦏", e2a68f), +_c(U+2990, "⦐", e2a690), +_c(U+2991, "⦑", e2a691), +_c(U+2992, "⦒", e2a692), +_c(U+2993, "⦓", e2a693), +_c(U+2994, "⦔", e2a694), +_c(U+2995, "⦕", e2a695), +_c(U+2996, "⦖", e2a696), +_c(U+2997, "⦗", e2a697), +_c(U+2998, "⦘", e2a698), +_c(U+2999, "⦙", e2a699), +_c(U+299A, "⦚", e2a69a), +_c(U+299B, "⦛", e2a69b), +_c(U+299C, "⦜", e2a69c), +_c(U+299D, "⦝", e2a69d), +_c(U+299E, "⦞", e2a69e), +_c(U+299F, "⦟", e2a69f), +_c(U+29A0, "⦠", e2a6a0), +_c(U+29A1, "⦡", e2a6a1), +_c(U+29A2, "⦢", e2a6a2), +_c(U+29A3, "⦣", e2a6a3), +_c(U+29A4, "⦤", e2a6a4), +_c(U+29A5, "⦥", e2a6a5), +_c(U+29A6, "⦦", e2a6a6), +_c(U+29A7, "⦧", e2a6a7), +_c(U+29A8, "⦨", e2a6a8), +_c(U+29A9, "⦩", e2a6a9), +_c(U+29AA, "⦪", e2a6aa), +_c(U+29AB, "⦫", e2a6ab), +_c(U+29AC, "⦬", e2a6ac), +_c(U+29AD, "⦭", e2a6ad), +_c(U+29AE, "⦮", e2a6ae), +_c(U+29AF, "⦯", e2a6af), +_c(U+29B0, "⦰", e2a6b0), +_c(U+29B1, "⦱", e2a6b1), +_c(U+29B2, "⦲", e2a6b2), +_c(U+29B3, "⦳", e2a6b3), +_c(U+29B4, "⦴", e2a6b4), +_c(U+29B5, "⦵", e2a6b5), +_c(U+29B6, "⦶", e2a6b6), +_c(U+29B7, "⦷", e2a6b7), +_c(U+29B8, "⦸", e2a6b8), +_c(U+29B9, "⦹", e2a6b9), +_c(U+29BA, "⦺", e2a6ba), +_c(U+29BB, "⦻", e2a6bb), +_c(U+29BC, "⦼", e2a6bc), +_c(U+29BD, "⦽", e2a6bd), +_c(U+29BE, "⦾", e2a6be), +_c(U+29BF, "⦿", e2a6bf), +_c(U+29C0, "⧀", e2a780), +_c(U+29C1, "⧁", e2a781), +_c(U+29C2, "⧂", e2a782), +_c(U+29C3, "⧃", e2a783), +_c(U+29C4, "⧄", e2a784), +_c(U+29C5, "⧅", e2a785), +_c(U+29C6, "⧆", e2a786), +_c(U+29C7, "⧇", e2a787), +_c(U+29C8, "⧈", e2a788), +_c(U+29C9, "⧉", e2a789), +_c(U+29CA, "⧊", e2a78a), +_c(U+29CB, "⧋", e2a78b), +_c(U+29CC, "⧌", e2a78c), +_c(U+29CD, "⧍", e2a78d), +_c(U+29CE, "⧎", e2a78e), +_c(U+29CF, "⧏", e2a78f), +_c(U+29D0, "⧐", e2a790), +_c(U+29D1, "⧑", e2a791), +_c(U+29D2, "⧒", e2a792), +_c(U+29D3, "⧓", e2a793), +_c(U+29D4, "⧔", e2a794), +_c(U+29D5, "⧕", e2a795), +_c(U+29D6, "⧖", e2a796), +_c(U+29D7, "⧗", e2a797), +_c(U+29D8, "⧘", e2a798), +_c(U+29D9, "⧙", e2a799), +_c(U+29DA, "⧚", e2a79a), +_c(U+29DB, "⧛", e2a79b), +_c(U+29DC, "⧜", e2a79c), +_c(U+29DD, "⧝", e2a79d), +_c(U+29DE, "⧞", e2a79e), +_c(U+29DF, "⧟", e2a79f), +_c(U+29E0, "⧠", e2a7a0), +_c(U+29E1, "⧡", e2a7a1), +_c(U+29E2, "⧢", e2a7a2), +_c(U+29E3, "⧣", e2a7a3), +_c(U+29E4, "⧤", e2a7a4), +_c(U+29E5, "⧥", e2a7a5), +_c(U+29E6, "⧦", e2a7a6), +_c(U+29E7, "⧧", e2a7a7), +_c(U+29E8, "⧨", e2a7a8), +_c(U+29E9, "⧩", e2a7a9), +_c(U+29EA, "⧪", e2a7aa), +_c(U+29EB, "⧫", e2a7ab), +_c(U+29EC, "⧬", e2a7ac), +_c(U+29ED, "⧭", e2a7ad), +_c(U+29EE, "⧮", e2a7ae), +_c(U+29EF, "⧯", e2a7af), +_c(U+29F0, "⧰", e2a7b0), +_c(U+29F1, "⧱", e2a7b1), +_c(U+29F2, "⧲", e2a7b2), +_c(U+29F3, "⧳", e2a7b3), +_c(U+29F4, "⧴", e2a7b4), +_c(U+29F5, "⧵", e2a7b5), +_c(U+29F6, "⧶", e2a7b6), +_c(U+29F7, "⧷", e2a7b7), +_c(U+29F8, "⧸", e2a7b8), +_c(U+29F9, "⧹", e2a7b9), +_c(U+29FA, "⧺", e2a7ba), +_c(U+29FB, "⧻", e2a7bb), +_c(U+29FC, "⧼", e2a7bc), +_c(U+29FD, "⧽", e2a7bd), +_c(U+29FE, "⧾", e2a7be), +_c(U+29FF, "⧿", e2a7bf), +_c(U+1D100, "𝄀", f09d8480), +_c(U+1D101, "𝄁", f09d8481), +_c(U+1D102, "𝄂", f09d8482), +_c(U+1D103, "𝄃", f09d8483), +_c(U+1D104, "𝄄", f09d8484), +_c(U+1D105, "𝄅", f09d8485), +_c(U+1D106, "𝄆", f09d8486), +_c(U+1D107, "𝄇", f09d8487), +_c(U+1D108, "𝄈", f09d8488), +_c(U+1D109, "𝄉", f09d8489), +_c(U+1D10A, "𝄊", f09d848a), +_c(U+1D10B, "𝄋", f09d848b), +_c(U+1D10C, "𝄌", f09d848c), +_c(U+1D10D, "𝄍", f09d848d), +_c(U+1D10E, "𝄎", f09d848e), +_c(U+1D10F, "𝄏", f09d848f), +_c(U+1D110, "𝄐", f09d8490), +_c(U+1D111, "𝄑", f09d8491), +_c(U+1D112, "𝄒", f09d8492), +_c(U+1D113, "𝄓", f09d8493), +_c(U+1D114, "𝄔", f09d8494), +_c(U+1D115, "𝄕", f09d8495), +_c(U+1D116, "𝄖", f09d8496), +_c(U+1D117, "𝄗", f09d8497), +_c(U+1D118, "𝄘", f09d8498), +_c(U+1D119, "𝄙", f09d8499), +_c(U+1D11A, "𝄚", f09d849a), +_c(U+1D11B, "𝄛", f09d849b), +_c(U+1D11C, "𝄜", f09d849c), +_c(U+1D11D, "𝄝", f09d849d), +_c(U+1D11E, "𝄞", f09d849e), +_c(U+1D11F, "𝄟", f09d849f), +_c(U+1D120, "𝄠", f09d84a0), +_c(U+1D121, "𝄡", f09d84a1), +_c(U+1D122, "𝄢", f09d84a2), +_c(U+1D123, "𝄣", f09d84a3), +_c(U+1D124, "𝄤", f09d84a4), +_c(U+1D125, "𝄥", f09d84a5), +_c(U+1D126, "𝄦", f09d84a6), +_c(U+1D127, "", f09d84a7), +_c(U+1D128, "", f09d84a8), +_c(U+1D129, "𝄩", f09d84a9), +_c(U+1D12A, "𝄪", f09d84aa), +_c(U+1D12B, "𝄫", f09d84ab), +_c(U+1D12C, "𝄬", f09d84ac), +_c(U+1D12D, "𝄭", f09d84ad), +_c(U+1D12E, "𝄮", f09d84ae), +_c(U+1D12F, "𝄯", f09d84af), +_c(U+1D130, "𝄰", f09d84b0), +_c(U+1D131, "𝄱", f09d84b1), +_c(U+1D132, "𝄲", f09d84b2), +_c(U+1D133, "𝄳", f09d84b3), +_c(U+1D134, "𝄴", f09d84b4), +_c(U+1D135, "𝄵", f09d84b5), +_c(U+1D136, "𝄶", f09d84b6), +_c(U+1D137, "𝄷", f09d84b7), +_c(U+1D138, "𝄸", f09d84b8), +_c(U+1D139, "𝄹", f09d84b9), +_c(U+1D13A, "𝄺", f09d84ba), +_c(U+1D13B, "𝄻", f09d84bb), +_c(U+1D13C, "𝄼", f09d84bc), +_c(U+1D13D, "𝄽", f09d84bd), +_c(U+1D13E, "𝄾", f09d84be), +_c(U+1D13F, "𝄿", f09d84bf), +_c(U+1D140, "𝅀", f09d8580), +_c(U+1D141, "𝅁", f09d8581), +_c(U+1D142, "𝅂", f09d8582), +_c(U+1D143, "𝅃", f09d8583), +_c(U+1D144, "𝅄", f09d8584), +_c(U+1D145, "𝅅", f09d8585), +_c(U+1D146, "𝅆", f09d8586), +_c(U+1D147, "𝅇", f09d8587), +_c(U+1D148, "𝅈", f09d8588), +_c(U+1D149, "𝅉", f09d8589), +_c(U+1D14A, "𝅊", f09d858a), +_c(U+1D14B, "𝅋", f09d858b), +_c(U+1D14C, "𝅌", f09d858c), +_c(U+1D14D, "𝅍", f09d858d), +_c(U+1D14E, "𝅎", f09d858e), +_c(U+1D14F, "𝅏", f09d858f), +_c(U+1D150, "𝅐", f09d8590), +_c(U+1D151, "𝅑", f09d8591), +_c(U+1D152, "𝅒", f09d8592), +_c(U+1D153, "𝅓", f09d8593), +_c(U+1D154, "𝅔", f09d8594), +_c(U+1D155, "𝅕", f09d8595), +_c(U+1D156, "𝅖", f09d8596), +_c(U+1D157, "𝅗", f09d8597), +_c(U+1D158, "𝅘", f09d8598), +_c(U+1D159, "𝅙", f09d8599), +_c(U+1D15A, "𝅚", f09d859a), +_c(U+1D15B, "𝅛", f09d859b), +_c(U+1D15C, "𝅜", f09d859c), +_c(U+1D15D, "𝅝", f09d859d), +_c(U+1D15E, "𝅗𝅥", f09d859e), +_c(U+1D15F, "𝅘𝅥", f09d859f), +_c(U+1D160, "𝅘𝅥𝅮", f09d85a0), +_c(U+1D161, "𝅘𝅥𝅯", f09d85a1), +_c(U+1D162, "𝅘𝅥𝅰", f09d85a2), +_c(U+1D163, "𝅘𝅥𝅱", f09d85a3), +_c(U+1D164, "𝅘𝅥𝅲", f09d85a4), +_c(U+1D165, "𝅥", f09d85a5), +_c(U+1D166, "𝅦", f09d85a6), +_c(U+1D167, "𝅧", f09d85a7), +_c(U+1D168, "𝅨", f09d85a8), +_c(U+1D169, "𝅩", f09d85a9), +_c(U+1D16A, "𝅪", f09d85aa), +_c(U+1D16B, "𝅫", f09d85ab), +_c(U+1D16C, "𝅬", f09d85ac), +_c(U+1D16D, "𝅭", f09d85ad), +_c(U+1D16E, "𝅮", f09d85ae), +_c(U+1D16F, "𝅯", f09d85af), +_c(U+1D170, "𝅰", f09d85b0), +_c(U+1D171, "𝅱", f09d85b1), +_c(U+1D172, "𝅲", f09d85b2), +_c(U+1D173, "", f09d85b3), +_c(U+1D174, "", f09d85b4), +_c(U+1D175, "", f09d85b5), +_c(U+1D176, "", f09d85b6), +_c(U+1D177, "", f09d85b7), +_c(U+1D178, "", f09d85b8), +_c(U+1D179, "", f09d85b9), +_c(U+1D17A, "", f09d85ba), +_c(U+1D17B, "𝅻", f09d85bb), +_c(U+1D17C, "𝅼", f09d85bc), +_c(U+1D17D, "𝅽", f09d85bd), +_c(U+1D17E, "𝅾", f09d85be), +_c(U+1D17F, "𝅿", f09d85bf), +_c(U+1D180, "𝆀", f09d8680), +_c(U+1D181, "𝆁", f09d8681), +_c(U+1D182, "𝆂", f09d8682), +_c(U+1D183, "𝆃", f09d8683), +_c(U+1D184, "𝆄", f09d8684), +_c(U+1D185, "𝆅", f09d8685), +_c(U+1D186, "𝆆", f09d8686), +_c(U+1D187, "𝆇", f09d8687), +_c(U+1D188, "𝆈", f09d8688), +_c(U+1D189, "𝆉", f09d8689), +_c(U+1D18A, "𝆊", f09d868a), +_c(U+1D18B, "𝆋", f09d868b), +_c(U+1D18C, "𝆌", f09d868c), +_c(U+1D18D, "𝆍", f09d868d), +_c(U+1D18E, "𝆎", f09d868e), +_c(U+1D18F, "𝆏", f09d868f), +_c(U+1D190, "𝆐", f09d8690), +_c(U+1D191, "𝆑", f09d8691), +_c(U+1D192, "𝆒", f09d8692), +_c(U+1D193, "𝆓", f09d8693), +_c(U+1D194, "𝆔", f09d8694), +_c(U+1D195, "𝆕", f09d8695), +_c(U+1D196, "𝆖", f09d8696), +_c(U+1D197, "𝆗", f09d8697), +_c(U+1D198, "𝆘", f09d8698), +_c(U+1D199, "𝆙", f09d8699), +_c(U+1D19A, "𝆚", f09d869a), +_c(U+1D19B, "𝆛", f09d869b), +_c(U+1D19C, "𝆜", f09d869c), +_c(U+1D19D, "𝆝", f09d869d), +_c(U+1D19E, "𝆞", f09d869e), +_c(U+1D19F, "𝆟", f09d869f), +_c(U+1D1A0, "𝆠", f09d86a0), +_c(U+1D1A1, "𝆡", f09d86a1), +_c(U+1D1A2, "𝆢", f09d86a2), +_c(U+1D1A3, "𝆣", f09d86a3), +_c(U+1D1A4, "𝆤", f09d86a4), +_c(U+1D1A5, "𝆥", f09d86a5), +_c(U+1D1A6, "𝆦", f09d86a6), +_c(U+1D1A7, "𝆧", f09d86a7), +_c(U+1D1A8, "𝆨", f09d86a8), +_c(U+1D1A9, "𝆩", f09d86a9), +_c(U+1D1AA, "𝆪", f09d86aa), +_c(U+1D1AB, "𝆫", f09d86ab), +_c(U+1D1AC, "𝆬", f09d86ac), +_c(U+1D1AD, "𝆭", f09d86ad), +_c(U+1D1AE, "𝆮", f09d86ae), +_c(U+1D1AF, "𝆯", f09d86af), +_c(U+1D1B0, "𝆰", f09d86b0), +_c(U+1D1B1, "𝆱", f09d86b1), +_c(U+1D1B2, "𝆲", f09d86b2), +_c(U+1D1B3, "𝆳", f09d86b3), +_c(U+1D1B4, "𝆴", f09d86b4), +_c(U+1D1B5, "𝆵", f09d86b5), +_c(U+1D1B6, "𝆶", f09d86b6), +_c(U+1D1B7, "𝆷", f09d86b7), +_c(U+1D1B8, "𝆸", f09d86b8), +_c(U+1D1B9, "𝆹", f09d86b9), +_c(U+1D1BA, "𝆺", f09d86ba), +_c(U+1D1BB, "𝆹𝅥", f09d86bb), +_c(U+1D1BC, "𝆺𝅥", f09d86bc), +_c(U+1D1BD, "𝆹𝅥𝅮", f09d86bd), +_c(U+1D1BE, "𝆺𝅥𝅮", f09d86be), +_c(U+1D1BF, "𝆹𝅥𝅯", f09d86bf), +_c(U+1D1C0, "𝆺𝅥𝅯", f09d8780), +_c(U+1D1C1, "𝇁", f09d8781), +_c(U+1D1C2, "𝇂", f09d8782), +_c(U+1D1C3, "𝇃", f09d8783), +_c(U+1D1C4, "𝇄", f09d8784), +_c(U+1D1C5, "𝇅", f09d8785), +_c(U+1D1C6, "𝇆", f09d8786), +_c(U+1D1C7, "𝇇", f09d8787), +_c(U+1D1C8, "𝇈", f09d8788), +_c(U+1D1C9, "𝇉", f09d8789), +_c(U+1D1CA, "𝇊", f09d878a), +_c(U+1D1CB, "𝇋", f09d878b), +_c(U+1D1CC, "𝇌", f09d878c), +_c(U+1D1CD, "𝇍", f09d878d), +_c(U+1D1CE, "𝇎", f09d878e), +_c(U+1D1CF, "𝇏", f09d878f), +_c(U+1D1D0, "𝇐", f09d8790), +_c(U+1D1D1, "𝇑", f09d8791), +_c(U+1D1D2, "𝇒", f09d8792), +_c(U+1D1D3, "𝇓", f09d8793), +_c(U+1D1D4, "𝇔", f09d8794), +_c(U+1D1D5, "𝇕", f09d8795), +_c(U+1D1D6, "𝇖", f09d8796), +_c(U+1D1D7, "𝇗", f09d8797), +_c(U+1D1D8, "𝇘", f09d8798), +_c(U+1D1D9, "𝇙", f09d8799), +_c(U+1D1DA, "𝇚", f09d879a), +_c(U+1D1DB, "𝇛", f09d879b), +_c(U+1D1DC, "𝇜", f09d879c), +_c(U+1D1DD, "𝇝", f09d879d), +_c(U+1D1DE, "𝇞", f09d879e), +_c(U+1D1DF, "𝇟", f09d879f), +_c(U+1D1E0, "𝇠", f09d87a0), +_c(U+1D1E1, "𝇡", f09d87a1), +_c(U+1D1E2, "𝇢", f09d87a2), +_c(U+1D1E3, "𝇣", f09d87a3), +_c(U+1D1E4, "𝇤", f09d87a4), +_c(U+1D1E5, "𝇥", f09d87a5), +_c(U+1D1E6, "𝇦", f09d87a6), +_c(U+1D1E7, "𝇧", f09d87a7), +_c(U+1D1E8, "𝇨", f09d87a8), +_c(U+1D1E9, "𝇩", f09d87a9), +_c(U+1D1EA, "𝇪", f09d87aa), +_c(U+1D1EB, "", f09d87ab), +_c(U+1D1EC, "", f09d87ac), +_c(U+1D1ED, "", f09d87ad), +_c(U+1D1EE, "", f09d87ae), +_c(U+1D1EF, "", f09d87af), +_c(U+1D1F0, "", f09d87b0), +_c(U+1D1F1, "", f09d87b1), +_c(U+1D1F2, "", f09d87b2), +_c(U+1D1F3, "", f09d87b3), +_c(U+1D1F4, "", f09d87b4), +_c(U+1D1F5, "", f09d87b5), +_c(U+1D1F6, "", f09d87b6), +_c(U+1D1F7, "", f09d87b7), +_c(U+1D1F8, "", f09d87b8), +_c(U+1D1F9, "", f09d87b9), +_c(U+1D1FA, "", f09d87ba), +_c(U+1D1FB, "", f09d87bb), +_c(U+1D1FC, "", f09d87bc), +_c(U+1D1FD, "", f09d87bd), +_c(U+1D1FE, "", f09d87be), +_c(U+1D1FF, "", f09d87bf), +_c(U+1D200, "𝈀", f09d8880), +_c(U+1D201, "𝈁", f09d8881), +_c(U+1D202, "𝈂", f09d8882), +_c(U+1D203, "𝈃", f09d8883), +_c(U+1D204, "𝈄", f09d8884), +_c(U+1D205, "𝈅", f09d8885), +_c(U+1D206, "𝈆", f09d8886), +_c(U+1D207, "𝈇", f09d8887), +_c(U+1D208, "𝈈", f09d8888), +_c(U+1D209, "𝈉", f09d8889), +_c(U+1D20A, "𝈊", f09d888a), +_c(U+1D20B, "𝈋", f09d888b), +_c(U+1D20C, "𝈌", f09d888c), +_c(U+1D20D, "𝈍", f09d888d), +_c(U+1D20E, "𝈎", f09d888e), +_c(U+1D20F, "𝈏", f09d888f), +_c(U+1D210, "𝈐", f09d8890), +_c(U+1D211, "𝈑", f09d8891), +_c(U+1D212, "𝈒", f09d8892), +_c(U+1D213, "𝈓", f09d8893), +_c(U+1D214, "𝈔", f09d8894), +_c(U+1D215, "𝈕", f09d8895), +_c(U+1D216, "𝈖", f09d8896), +_c(U+1D217, "𝈗", f09d8897), +_c(U+1D218, "𝈘", f09d8898), +_c(U+1D219, "𝈙", f09d8899), +_c(U+1D21A, "𝈚", f09d889a), +_c(U+1D21B, "𝈛", f09d889b), +_c(U+1D21C, "𝈜", f09d889c), +_c(U+1D21D, "𝈝", f09d889d), +_c(U+1D21E, "𝈞", f09d889e), +_c(U+1D21F, "𝈟", f09d889f), +_c(U+1D220, "𝈠", f09d88a0), +_c(U+1D221, "𝈡", f09d88a1), +_c(U+1D222, "𝈢", f09d88a2), +_c(U+1D223, "𝈣", f09d88a3), +_c(U+1D224, "𝈤", f09d88a4), +_c(U+1D225, "𝈥", f09d88a5), +_c(U+1D226, "𝈦", f09d88a6), +_c(U+1D227, "𝈧", f09d88a7), +_c(U+1D228, "𝈨", f09d88a8), +_c(U+1D229, "𝈩", f09d88a9), +_c(U+1D22A, "𝈪", f09d88aa), +_c(U+1D22B, "𝈫", f09d88ab), +_c(U+1D22C, "𝈬", f09d88ac), +_c(U+1D22D, "𝈭", f09d88ad), +_c(U+1D22E, "𝈮", f09d88ae), +_c(U+1D22F, "𝈯", f09d88af), +_c(U+1D230, "𝈰", f09d88b0), +_c(U+1D231, "𝈱", f09d88b1), +_c(U+1D232, "𝈲", f09d88b2), +_c(U+1D233, "𝈳", f09d88b3), +_c(U+1D234, "𝈴", f09d88b4), +_c(U+1D235, "𝈵", f09d88b5), +_c(U+1D236, "𝈶", f09d88b6), +_c(U+1D237, "𝈷", f09d88b7), +_c(U+1D238, "𝈸", f09d88b8), +_c(U+1D239, "𝈹", f09d88b9), +_c(U+1D23A, "𝈺", f09d88ba), +_c(U+1D23B, "𝈻", f09d88bb), +_c(U+1D23C, "𝈼", f09d88bc), +_c(U+1D23D, "𝈽", f09d88bd), +_c(U+1D23E, "𝈾", f09d88be), +_c(U+1D23F, "𝈿", f09d88bf), +_c(U+1D240, "𝉀", f09d8980), +_c(U+1D241, "𝉁", f09d8981), +_c(U+1D242, "𝉂", f09d8982), +_c(U+1D243, "𝉃", f09d8983), +_c(U+1D244, "𝉄", f09d8984), +_c(U+1D245, "𝉅", f09d8985), +_c(U+1D246, "", f09d8986), +_c(U+1D247, "", f09d8987), +_c(U+1D248, "", f09d8988), +_c(U+1D249, "", f09d8989), +_c(U+1D24A, "", f09d898a), +_c(U+1D24B, "", f09d898b), +_c(U+1D24C, "", f09d898c), +_c(U+1D24D, "", f09d898d), +_c(U+1D24E, "", f09d898e), +_c(U+1D24F, "", f09d898f), +_c(U+1D250, "", f09d8990), +_c(U+1D251, "", f09d8991), +_c(U+1D252, "", f09d8992), +_c(U+1D253, "", f09d8993), +_c(U+1D254, "", f09d8994), +_c(U+1D255, "", f09d8995), +_c(U+1D256, "", f09d8996), +_c(U+1D257, "", f09d8997), +_c(U+1D258, "", f09d8998), +_c(U+1D259, "", f09d8999), +_c(U+1D25A, "", f09d899a), +_c(U+1D25B, "", f09d899b), +_c(U+1D25C, "", f09d899c), +_c(U+1D25D, "", f09d899d), +_c(U+1D25E, "", f09d899e), +_c(U+1D25F, "", f09d899f), +_c(U+1D260, "", f09d89a0), +_c(U+1D261, "", f09d89a1), +_c(U+1D262, "", f09d89a2), +_c(U+1D263, "", f09d89a3), +_c(U+1D264, "", f09d89a4), +_c(U+1D265, "", f09d89a5), +_c(U+1D266, "", f09d89a6), +_c(U+1D267, "", f09d89a7), +_c(U+1D268, "", f09d89a8), +_c(U+1D269, "", f09d89a9), +_c(U+1D26A, "", f09d89aa), +_c(U+1D26B, "", f09d89ab), +_c(U+1D26C, "", f09d89ac), +_c(U+1D26D, "", f09d89ad), +_c(U+1D26E, "", f09d89ae), +_c(U+1D26F, "", f09d89af), +_c(U+1D270, "", f09d89b0), +_c(U+1D271, "", f09d89b1), +_c(U+1D272, "", f09d89b2), +_c(U+1D273, "", f09d89b3), +_c(U+1D274, "", f09d89b4), +_c(U+1D275, "", f09d89b5), +_c(U+1D276, "", f09d89b6), +_c(U+1D277, "", f09d89b7), +_c(U+1D278, "", f09d89b8), +_c(U+1D279, "", f09d89b9), +_c(U+1D27A, "", f09d89ba), +_c(U+1D27B, "", f09d89bb), +_c(U+1D27C, "", f09d89bc), +_c(U+1D27D, "", f09d89bd), +_c(U+1D27E, "", f09d89be), +_c(U+1D27F, "", f09d89bf), +_c(U+1D280, "", f09d8a80), +_c(U+1D281, "", f09d8a81), +_c(U+1D282, "", f09d8a82), +_c(U+1D283, "", f09d8a83), +_c(U+1D284, "", f09d8a84), +_c(U+1D285, "", f09d8a85), +_c(U+1D286, "", f09d8a86), +_c(U+1D287, "", f09d8a87), +_c(U+1D288, "", f09d8a88), +_c(U+1D289, "", f09d8a89), +_c(U+1D28A, "", f09d8a8a), +_c(U+1D28B, "", f09d8a8b), +_c(U+1D28C, "", f09d8a8c), +_c(U+1D28D, "", f09d8a8d), +_c(U+1D28E, "", f09d8a8e), +_c(U+1D28F, "", f09d8a8f), +_c(U+1D290, "", f09d8a90), +_c(U+1D291, "", f09d8a91), +_c(U+1D292, "", f09d8a92), +_c(U+1D293, "", f09d8a93), +_c(U+1D294, "", f09d8a94), +_c(U+1D295, "", f09d8a95), +_c(U+1D296, "", f09d8a96), +_c(U+1D297, "", f09d8a97), +_c(U+1D298, "", f09d8a98), +_c(U+1D299, "", f09d8a99), +_c(U+1D29A, "", f09d8a9a), +_c(U+1D29B, "", f09d8a9b), +_c(U+1D29C, "", f09d8a9c), +_c(U+1D29D, "", f09d8a9d), +_c(U+1D29E, "", f09d8a9e), +_c(U+1D29F, "", f09d8a9f), +_c(U+1D2A0, "", f09d8aa0), +_c(U+1D2A1, "", f09d8aa1), +_c(U+1D2A2, "", f09d8aa2), +_c(U+1D2A3, "", f09d8aa3), +_c(U+1D2A4, "", f09d8aa4), +_c(U+1D2A5, "", f09d8aa5), +_c(U+1D2A6, "", f09d8aa6), +_c(U+1D2A7, "", f09d8aa7), +_c(U+1D2A8, "", f09d8aa8), +_c(U+1D2A9, "", f09d8aa9), +_c(U+1D2AA, "", f09d8aaa), +_c(U+1D2AB, "", f09d8aab), +_c(U+1D2AC, "", f09d8aac), +_c(U+1D2AD, "", f09d8aad), +_c(U+1D2AE, "", f09d8aae), +_c(U+1D2AF, "", f09d8aaf), +_c(U+1D2B0, "", f09d8ab0), +_c(U+1D2B1, "", f09d8ab1), +_c(U+1D2B2, "", f09d8ab2), +_c(U+1D2B3, "", f09d8ab3), +_c(U+1D2B4, "", f09d8ab4), +_c(U+1D2B5, "", f09d8ab5), +_c(U+1D2B6, "", f09d8ab6), +_c(U+1D2B7, "", f09d8ab7), +_c(U+1D2B8, "", f09d8ab8), +_c(U+1D2B9, "", f09d8ab9), +_c(U+1D2BA, "", f09d8aba), +_c(U+1D2BB, "", f09d8abb), +_c(U+1D2BC, "", f09d8abc), +_c(U+1D2BD, "", f09d8abd), +_c(U+1D2BE, "", f09d8abe), +_c(U+1D2BF, "", f09d8abf), +_c(U+1D2C0, "𝋀", f09d8b80), +_c(U+1D2C1, "𝋁", f09d8b81), +_c(U+1D2C2, "𝋂", f09d8b82), +_c(U+1D2C3, "𝋃", f09d8b83), +_c(U+1D2C4, "𝋄", f09d8b84), +_c(U+1D2C5, "𝋅", f09d8b85), +_c(U+1D2C6, "𝋆", f09d8b86), +_c(U+1D2C7, "𝋇", f09d8b87), +_c(U+1D2C8, "𝋈", f09d8b88), +_c(U+1D2C9, "𝋉", f09d8b89), +_c(U+1D2CA, "𝋊", f09d8b8a), +_c(U+1D2CB, "𝋋", f09d8b8b), +_c(U+1D2CC, "𝋌", f09d8b8c), +_c(U+1D2CD, "𝋍", f09d8b8d), +_c(U+1D2CE, "𝋎", f09d8b8e), +_c(U+1D2CF, "𝋏", f09d8b8f), +_c(U+1D2D0, "𝋐", f09d8b90), +_c(U+1D2D1, "𝋑", f09d8b91), +_c(U+1D2D2, "𝋒", f09d8b92), +_c(U+1D2D3, "𝋓", f09d8b93), +_c(U+1D2D4, "", f09d8b94), +_c(U+1D2D5, "", f09d8b95), +_c(U+1D2D6, "", f09d8b96), +_c(U+1D2D7, "", f09d8b97), +_c(U+1D2D8, "", f09d8b98), +_c(U+1D2D9, "", f09d8b99), +_c(U+1D2DA, "", f09d8b9a), +_c(U+1D2DB, "", f09d8b9b), +_c(U+1D2DC, "", f09d8b9c), +_c(U+1D2DD, "", f09d8b9d), +_c(U+1D2DE, "", f09d8b9e), +_c(U+1D2DF, "", f09d8b9f), +_c(U+1D2E0, "𝋠", f09d8ba0), +_c(U+1D2E1, "𝋡", f09d8ba1), +_c(U+1D2E2, "𝋢", f09d8ba2), +_c(U+1D2E3, "𝋣", f09d8ba3), +_c(U+1D2E4, "𝋤", f09d8ba4), +_c(U+1D2E5, "𝋥", f09d8ba5), +_c(U+1D2E6, "𝋦", f09d8ba6), +_c(U+1D2E7, "𝋧", f09d8ba7), +_c(U+1D2E8, "𝋨", f09d8ba8), +_c(U+1D2E9, "𝋩", f09d8ba9), +_c(U+1D2EA, "𝋪", f09d8baa), +_c(U+1D2EB, "𝋫", f09d8bab), +_c(U+1D2EC, "𝋬", f09d8bac), +_c(U+1D2ED, "𝋭", f09d8bad), +_c(U+1D2EE, "𝋮", f09d8bae), +_c(U+1D2EF, "𝋯", f09d8baf), +_c(U+1D2F0, "𝋰", f09d8bb0), +_c(U+1D2F1, "𝋱", f09d8bb1), +_c(U+1D2F2, "𝋲", f09d8bb2), +_c(U+1D2F3, "𝋳", f09d8bb3), +_c(U+1D2F4, "", f09d8bb4), +_c(U+1D2F5, "", f09d8bb5), +_c(U+1D2F6, "", f09d8bb6), +_c(U+1D2F7, "", f09d8bb7), +_c(U+1D2F8, "", f09d8bb8), +_c(U+1D2F9, "", f09d8bb9), +_c(U+1D2FA, "", f09d8bba), +_c(U+1D2FB, "", f09d8bbb), +_c(U+1D2FC, "", f09d8bbc), +_c(U+1D2FD, "", f09d8bbd), +_c(U+1D2FE, "", f09d8bbe), +_c(U+1D2FF, "", f09d8bbf), +_c(U+1D300, "𝌀", f09d8c80), +_c(U+1D301, "𝌁", f09d8c81), +_c(U+1D302, "𝌂", f09d8c82), +_c(U+1D303, "𝌃", f09d8c83), +_c(U+1D304, "𝌄", f09d8c84), +_c(U+1D305, "𝌅", f09d8c85), +_c(U+1D306, "𝌆", f09d8c86), +_c(U+1D307, "𝌇", f09d8c87), +_c(U+1D308, "𝌈", f09d8c88), +_c(U+1D309, "𝌉", f09d8c89), +_c(U+1D30A, "𝌊", f09d8c8a), +_c(U+1D30B, "𝌋", f09d8c8b), +_c(U+1D30C, "𝌌", f09d8c8c), +_c(U+1D30D, "𝌍", f09d8c8d), +_c(U+1D30E, "𝌎", f09d8c8e), +_c(U+1D30F, "𝌏", f09d8c8f), +_c(U+1D310, "𝌐", f09d8c90), +_c(U+1D311, "𝌑", f09d8c91), +_c(U+1D312, "𝌒", f09d8c92), +_c(U+1D313, "𝌓", f09d8c93), +_c(U+1D314, "𝌔", f09d8c94), +_c(U+1D315, "𝌕", f09d8c95), +_c(U+1D316, "𝌖", f09d8c96), +_c(U+1D317, "𝌗", f09d8c97), +_c(U+1D318, "𝌘", f09d8c98), +_c(U+1D319, "𝌙", f09d8c99), +_c(U+1D31A, "𝌚", f09d8c9a), +_c(U+1D31B, "𝌛", f09d8c9b), +_c(U+1D31C, "𝌜", f09d8c9c), +_c(U+1D31D, "𝌝", f09d8c9d), +_c(U+1D31E, "𝌞", f09d8c9e), +_c(U+1D31F, "𝌟", f09d8c9f), +_c(U+1D320, "𝌠", f09d8ca0), +_c(U+1D321, "𝌡", f09d8ca1), +_c(U+1D322, "𝌢", f09d8ca2), +_c(U+1D323, "𝌣", f09d8ca3), +_c(U+1D324, "𝌤", f09d8ca4), +_c(U+1D325, "𝌥", f09d8ca5), +_c(U+1D326, "𝌦", f09d8ca6), +_c(U+1D327, "𝌧", f09d8ca7), +_c(U+1D328, "𝌨", f09d8ca8), +_c(U+1D329, "𝌩", f09d8ca9), +_c(U+1D32A, "𝌪", f09d8caa), +_c(U+1D32B, "𝌫", f09d8cab), +_c(U+1D32C, "𝌬", f09d8cac), +_c(U+1D32D, "𝌭", f09d8cad), +_c(U+1D32E, "𝌮", f09d8cae), +_c(U+1D32F, "𝌯", f09d8caf), +_c(U+1D330, "𝌰", f09d8cb0), +_c(U+1D331, "𝌱", f09d8cb1), +_c(U+1D332, "𝌲", f09d8cb2), +_c(U+1D333, "𝌳", f09d8cb3), +_c(U+1D334, "𝌴", f09d8cb4), +_c(U+1D335, "𝌵", f09d8cb5), +_c(U+1D336, "𝌶", f09d8cb6), +_c(U+1D337, "𝌷", f09d8cb7), +_c(U+1D338, "𝌸", f09d8cb8), +_c(U+1D339, "𝌹", f09d8cb9), +_c(U+1D33A, "𝌺", f09d8cba), +_c(U+1D33B, "𝌻", f09d8cbb), +_c(U+1D33C, "𝌼", f09d8cbc), +_c(U+1D33D, "𝌽", f09d8cbd), +_c(U+1D33E, "𝌾", f09d8cbe), +_c(U+1D33F, "𝌿", f09d8cbf), +_c(U+1D340, "𝍀", f09d8d80), +_c(U+1D341, "𝍁", f09d8d81), +_c(U+1D342, "𝍂", f09d8d82), +_c(U+1D343, "𝍃", f09d8d83), +_c(U+1D344, "𝍄", f09d8d84), +_c(U+1D345, "𝍅", f09d8d85), +_c(U+1D346, "𝍆", f09d8d86), +_c(U+1D347, "𝍇", f09d8d87), +_c(U+1D348, "𝍈", f09d8d88), +_c(U+1D349, "𝍉", f09d8d89), +_c(U+1D34A, "𝍊", f09d8d8a), +_c(U+1D34B, "𝍋", f09d8d8b), +_c(U+1D34C, "𝍌", f09d8d8c), +_c(U+1D34D, "𝍍", f09d8d8d), +_c(U+1D34E, "𝍎", f09d8d8e), +_c(U+1D34F, "𝍏", f09d8d8f), +_c(U+1D350, "𝍐", f09d8d90), +_c(U+1D351, "𝍑", f09d8d91), +_c(U+1D352, "𝍒", f09d8d92), +_c(U+1D353, "𝍓", f09d8d93), +_c(U+1D354, "𝍔", f09d8d94), +_c(U+1D355, "𝍕", f09d8d95), +_c(U+1D356, "𝍖", f09d8d96), +_c(U+1D357, "", f09d8d97), +_c(U+1D358, "", f09d8d98), +_c(U+1D359, "", f09d8d99), +_c(U+1D35A, "", f09d8d9a), +_c(U+1D35B, "", f09d8d9b), +_c(U+1D35C, "", f09d8d9c), +_c(U+1D35D, "", f09d8d9d), +_c(U+1D35E, "", f09d8d9e), +_c(U+1D35F, "", f09d8d9f), +_c(U+1D360, "𝍠", f09d8da0), +_c(U+1D361, "𝍡", f09d8da1), +_c(U+1D362, "𝍢", f09d8da2), +_c(U+1D363, "𝍣", f09d8da3), +_c(U+1D364, "𝍤", f09d8da4), +_c(U+1D365, "𝍥", f09d8da5), +_c(U+1D366, "𝍦", f09d8da6), +_c(U+1D367, "𝍧", f09d8da7), +_c(U+1D368, "𝍨", f09d8da8), +_c(U+1D369, "𝍩", f09d8da9), +_c(U+1D36A, "𝍪", f09d8daa), +_c(U+1D36B, "𝍫", f09d8dab), +_c(U+1D36C, "𝍬", f09d8dac), +_c(U+1D36D, "𝍭", f09d8dad), +_c(U+1D36E, "𝍮", f09d8dae), +_c(U+1D36F, "𝍯", f09d8daf), +_c(U+1D370, "𝍰", f09d8db0), +_c(U+1D371, "𝍱", f09d8db1), +_c(U+1D372, "𝍲", f09d8db2), +_c(U+1D373, "𝍳", f09d8db3), +_c(U+1D374, "𝍴", f09d8db4), +_c(U+1D375, "𝍵", f09d8db5), +_c(U+1D376, "𝍶", f09d8db6), +_c(U+1D377, "𝍷", f09d8db7), +_c(U+1D378, "𝍸", f09d8db8), +_c(U+1D379, "", f09d8db9), +_c(U+1D37A, "", f09d8dba), +_c(U+1D37B, "", f09d8dbb), +_c(U+1D37C, "", f09d8dbc), +_c(U+1D37D, "", f09d8dbd), +_c(U+1D37E, "", f09d8dbe), +_c(U+1D37F, "", f09d8dbf), +_c(U+1D380, "", f09d8e80), +_c(U+1D381, "", f09d8e81), +_c(U+1D382, "", f09d8e82), +_c(U+1D383, "", f09d8e83), +_c(U+1D384, "", f09d8e84), +_c(U+1D385, "", f09d8e85), +_c(U+1D386, "", f09d8e86), +_c(U+1D387, "", f09d8e87), +_c(U+1D388, "", f09d8e88), +_c(U+1D389, "", f09d8e89), +_c(U+1D38A, "", f09d8e8a), +_c(U+1D38B, "", f09d8e8b), +_c(U+1D38C, "", f09d8e8c), +_c(U+1D38D, "", f09d8e8d), +_c(U+1D38E, "", f09d8e8e), +_c(U+1D38F, "", f09d8e8f), +_c(U+1D390, "", f09d8e90), +_c(U+1D391, "", f09d8e91), +_c(U+1D392, "", f09d8e92), +_c(U+1D393, "", f09d8e93), +_c(U+1D394, "", f09d8e94), +_c(U+1D395, "", f09d8e95), +_c(U+1D396, "", f09d8e96), +_c(U+1D397, "", f09d8e97), +_c(U+1D398, "", f09d8e98), +_c(U+1D399, "", f09d8e99), +_c(U+1D39A, "", f09d8e9a), +_c(U+1D39B, "", f09d8e9b), +_c(U+1D39C, "", f09d8e9c), +_c(U+1D39D, "", f09d8e9d), +_c(U+1D39E, "", f09d8e9e), +_c(U+1D39F, "", f09d8e9f), +_c(U+1D3A0, "", f09d8ea0), +_c(U+1D3A1, "", f09d8ea1), +_c(U+1D3A2, "", f09d8ea2), +_c(U+1D3A3, "", f09d8ea3), +_c(U+1D3A4, "", f09d8ea4), +_c(U+1D3A5, "", f09d8ea5), +_c(U+1D3A6, "", f09d8ea6), +_c(U+1D3A7, "", f09d8ea7), +_c(U+1D3A8, "", f09d8ea8), +_c(U+1D3A9, "", f09d8ea9), +_c(U+1D3AA, "", f09d8eaa), +_c(U+1D3AB, "", f09d8eab), +_c(U+1D3AC, "", f09d8eac), +_c(U+1D3AD, "", f09d8ead), +_c(U+1D3AE, "", f09d8eae), +_c(U+1D3AF, "", f09d8eaf), +_c(U+1D3B0, "", f09d8eb0), +_c(U+1D3B1, "", f09d8eb1), +_c(U+1D3B2, "", f09d8eb2), +_c(U+1D3B3, "", f09d8eb3), +_c(U+1D3B4, "", f09d8eb4), +_c(U+1D3B5, "", f09d8eb5), +_c(U+1D3B6, "", f09d8eb6), +_c(U+1D3B7, "", f09d8eb7), +_c(U+1D3B8, "", f09d8eb8), +_c(U+1D3B9, "", f09d8eb9), +_c(U+1D3BA, "", f09d8eba), +_c(U+1D3BB, "", f09d8ebb), +_c(U+1D3BC, "", f09d8ebc), +_c(U+1D3BD, "", f09d8ebd), +_c(U+1D3BE, "", f09d8ebe), +_c(U+1D3BF, "", f09d8ebf), +_c(U+1D3C0, "", f09d8f80), +_c(U+1D3C1, "", f09d8f81), +_c(U+1D3C2, "", f09d8f82), +_c(U+1D3C3, "", f09d8f83), +_c(U+1D3C4, "", f09d8f84), +_c(U+1D3C5, "", f09d8f85), +_c(U+1D3C6, "", f09d8f86), +_c(U+1D3C7, "", f09d8f87), +_c(U+1D3C8, "", f09d8f88), +_c(U+1D3C9, "", f09d8f89), +_c(U+1D3CA, "", f09d8f8a), +_c(U+1D3CB, "", f09d8f8b), +_c(U+1D3CC, "", f09d8f8c), +_c(U+1D3CD, "", f09d8f8d), +_c(U+1D3CE, "", f09d8f8e), +_c(U+1D3CF, "", f09d8f8f), +_c(U+1D3D0, "", f09d8f90), +_c(U+1D3D1, "", f09d8f91), +_c(U+1D3D2, "", f09d8f92), +_c(U+1D3D3, "", f09d8f93), +_c(U+1D3D4, "", f09d8f94), +_c(U+1D3D5, "", f09d8f95), +_c(U+1D3D6, "", f09d8f96), +_c(U+1D3D7, "", f09d8f97), +_c(U+1D3D8, "", f09d8f98), +_c(U+1D3D9, "", f09d8f99), +_c(U+1D3DA, "", f09d8f9a), +_c(U+1D3DB, "", f09d8f9b), +_c(U+1D3DC, "", f09d8f9c), +_c(U+1D3DD, "", f09d8f9d), +_c(U+1D3DE, "", f09d8f9e), +_c(U+1D3DF, "", f09d8f9f), +_c(U+1D3E0, "", f09d8fa0), +_c(U+1D3E1, "", f09d8fa1), +_c(U+1D3E2, "", f09d8fa2), +_c(U+1D3E3, "", f09d8fa3), +_c(U+1D3E4, "", f09d8fa4), +_c(U+1D3E5, "", f09d8fa5), +_c(U+1D3E6, "", f09d8fa6), +_c(U+1D3E7, "", f09d8fa7), +_c(U+1D3E8, "", f09d8fa8), +_c(U+1D3E9, "", f09d8fa9), +_c(U+1D3EA, "", f09d8faa), +_c(U+1D3EB, "", f09d8fab), +_c(U+1D3EC, "", f09d8fac), +_c(U+1D3ED, "", f09d8fad), +_c(U+1D3EE, "", f09d8fae), +_c(U+1D3EF, "", f09d8faf), +_c(U+1D3F0, "", f09d8fb0), +_c(U+1D3F1, "", f09d8fb1), +_c(U+1D3F2, "", f09d8fb2), +_c(U+1D3F3, "", f09d8fb3), +_c(U+1D3F4, "", f09d8fb4), +_c(U+1D3F5, "", f09d8fb5), +_c(U+1D3F6, "", f09d8fb6), +_c(U+1D3F7, "", f09d8fb7), +_c(U+1D3F8, "", f09d8fb8), +_c(U+1D3F9, "", f09d8fb9), +_c(U+1D3FA, "", f09d8fba), +_c(U+1D3FB, "", f09d8fbb), +_c(U+1D3FC, "", f09d8fbc), +_c(U+1D3FD, "", f09d8fbd), +_c(U+1D3FE, "", f09d8fbe), +_c(U+1D3FF, "", f09d8fbf), +_c(U+1D400, "𝐀", f09d9080), +_c(U+1D401, "𝐁", f09d9081), +_c(U+1D402, "𝐂", f09d9082), +_c(U+1D403, "𝐃", f09d9083), +_c(U+1D404, "𝐄", f09d9084), +_c(U+1D405, "𝐅", f09d9085), +_c(U+1D406, "𝐆", f09d9086), +_c(U+1D407, "𝐇", f09d9087), +_c(U+1D408, "𝐈", f09d9088), +_c(U+1D409, "𝐉", f09d9089), +_c(U+1D40A, "𝐊", f09d908a), +_c(U+1D40B, "𝐋", f09d908b), +_c(U+1D40C, "𝐌", f09d908c), +_c(U+1D40D, "𝐍", f09d908d), +_c(U+1D40E, "𝐎", f09d908e), +_c(U+1D40F, "𝐏", f09d908f), +_c(U+1D410, "𝐐", f09d9090), +_c(U+1D411, "𝐑", f09d9091), +_c(U+1D412, "𝐒", f09d9092), +_c(U+1D413, "𝐓", f09d9093), +_c(U+1D414, "𝐔", f09d9094), +_c(U+1D415, "𝐕", f09d9095), +_c(U+1D416, "𝐖", f09d9096), +_c(U+1D417, "𝐗", f09d9097), +_c(U+1D418, "𝐘", f09d9098), +_c(U+1D419, "𝐙", f09d9099), +_c(U+1D41A, "𝐚", f09d909a), +_c(U+1D41B, "𝐛", f09d909b), +_c(U+1D41C, "𝐜", f09d909c), +_c(U+1D41D, "𝐝", f09d909d), +_c(U+1D41E, "𝐞", f09d909e), +_c(U+1D41F, "𝐟", f09d909f), +_c(U+1D420, "𝐠", f09d90a0), +_c(U+1D421, "𝐡", f09d90a1), +_c(U+1D422, "𝐢", f09d90a2), +_c(U+1D423, "𝐣", f09d90a3), +_c(U+1D424, "𝐤", f09d90a4), +_c(U+1D425, "𝐥", f09d90a5), +_c(U+1D426, "𝐦", f09d90a6), +_c(U+1D427, "𝐧", f09d90a7), +_c(U+1D428, "𝐨", f09d90a8), +_c(U+1D429, "𝐩", f09d90a9), +_c(U+1D42A, "𝐪", f09d90aa), +_c(U+1D42B, "𝐫", f09d90ab), +_c(U+1D42C, "𝐬", f09d90ac), +_c(U+1D42D, "𝐭", f09d90ad), +_c(U+1D42E, "𝐮", f09d90ae), +_c(U+1D42F, "𝐯", f09d90af), +_c(U+1D430, "𝐰", f09d90b0), +_c(U+1D431, "𝐱", f09d90b1), +_c(U+1D432, "𝐲", f09d90b2), +_c(U+1D433, "𝐳", f09d90b3), +_c(U+1D434, "𝐴", f09d90b4), +_c(U+1D435, "𝐵", f09d90b5), +_c(U+1D436, "𝐶", f09d90b6), +_c(U+1D437, "𝐷", f09d90b7), +_c(U+1D438, "𝐸", f09d90b8), +_c(U+1D439, "𝐹", f09d90b9), +_c(U+1D43A, "𝐺", f09d90ba), +_c(U+1D43B, "𝐻", f09d90bb), +_c(U+1D43C, "𝐼", f09d90bc), +_c(U+1D43D, "𝐽", f09d90bd), +_c(U+1D43E, "𝐾", f09d90be), +_c(U+1D43F, "𝐿", f09d90bf), +_c(U+1D440, "𝑀", f09d9180), +_c(U+1D441, "𝑁", f09d9181), +_c(U+1D442, "𝑂", f09d9182), +_c(U+1D443, "𝑃", f09d9183), +_c(U+1D444, "𝑄", f09d9184), +_c(U+1D445, "𝑅", f09d9185), +_c(U+1D446, "𝑆", f09d9186), +_c(U+1D447, "𝑇", f09d9187), +_c(U+1D448, "𝑈", f09d9188), +_c(U+1D449, "𝑉", f09d9189), +_c(U+1D44A, "𝑊", f09d918a), +_c(U+1D44B, "𝑋", f09d918b), +_c(U+1D44C, "𝑌", f09d918c), +_c(U+1D44D, "𝑍", f09d918d), +_c(U+1D44E, "𝑎", f09d918e), +_c(U+1D44F, "𝑏", f09d918f), +_c(U+1D450, "𝑐", f09d9190), +_c(U+1D451, "𝑑", f09d9191), +_c(U+1D452, "𝑒", f09d9192), +_c(U+1D453, "𝑓", f09d9193), +_c(U+1D454, "𝑔", f09d9194), +_c(U+1D455, "", f09d9195), +_c(U+1D456, "𝑖", f09d9196), +_c(U+1D457, "𝑗", f09d9197), +_c(U+1D458, "𝑘", f09d9198), +_c(U+1D459, "𝑙", f09d9199), +_c(U+1D45A, "𝑚", f09d919a), +_c(U+1D45B, "𝑛", f09d919b), +_c(U+1D45C, "𝑜", f09d919c), +_c(U+1D45D, "𝑝", f09d919d), +_c(U+1D45E, "𝑞", f09d919e), +_c(U+1D45F, "𝑟", f09d919f), +_c(U+1D460, "𝑠", f09d91a0), +_c(U+1D461, "𝑡", f09d91a1), +_c(U+1D462, "𝑢", f09d91a2), +_c(U+1D463, "𝑣", f09d91a3), +_c(U+1D464, "𝑤", f09d91a4), +_c(U+1D465, "𝑥", f09d91a5), +_c(U+1D466, "𝑦", f09d91a6), +_c(U+1D467, "𝑧", f09d91a7), +_c(U+1D468, "𝑨", f09d91a8), +_c(U+1D469, "𝑩", f09d91a9), +_c(U+1D46A, "𝑪", f09d91aa), +_c(U+1D46B, "𝑫", f09d91ab), +_c(U+1D46C, "𝑬", f09d91ac), +_c(U+1D46D, "𝑭", f09d91ad), +_c(U+1D46E, "𝑮", f09d91ae), +_c(U+1D46F, "𝑯", f09d91af), +_c(U+1D470, "𝑰", f09d91b0), +_c(U+1D471, "𝑱", f09d91b1), +_c(U+1D472, "𝑲", f09d91b2), +_c(U+1D473, "𝑳", f09d91b3), +_c(U+1D474, "𝑴", f09d91b4), +_c(U+1D475, "𝑵", f09d91b5), +_c(U+1D476, "𝑶", f09d91b6), +_c(U+1D477, "𝑷", f09d91b7), +_c(U+1D478, "𝑸", f09d91b8), +_c(U+1D479, "𝑹", f09d91b9), +_c(U+1D47A, "𝑺", f09d91ba), +_c(U+1D47B, "𝑻", f09d91bb), +_c(U+1D47C, "𝑼", f09d91bc), +_c(U+1D47D, "𝑽", f09d91bd), +_c(U+1D47E, "𝑾", f09d91be), +_c(U+1D47F, "𝑿", f09d91bf), +_c(U+1D480, "𝒀", f09d9280), +_c(U+1D481, "𝒁", f09d9281), +_c(U+1D482, "𝒂", f09d9282), +_c(U+1D483, "𝒃", f09d9283), +_c(U+1D484, "𝒄", f09d9284), +_c(U+1D485, "𝒅", f09d9285), +_c(U+1D486, "𝒆", f09d9286), +_c(U+1D487, "𝒇", f09d9287), +_c(U+1D488, "𝒈", f09d9288), +_c(U+1D489, "𝒉", f09d9289), +_c(U+1D48A, "𝒊", f09d928a), +_c(U+1D48B, "𝒋", f09d928b), +_c(U+1D48C, "𝒌", f09d928c), +_c(U+1D48D, "𝒍", f09d928d), +_c(U+1D48E, "𝒎", f09d928e), +_c(U+1D48F, "𝒏", f09d928f), +_c(U+1D490, "𝒐", f09d9290), +_c(U+1D491, "𝒑", f09d9291), +_c(U+1D492, "𝒒", f09d9292), +_c(U+1D493, "𝒓", f09d9293), +_c(U+1D494, "𝒔", f09d9294), +_c(U+1D495, "𝒕", f09d9295), +_c(U+1D496, "𝒖", f09d9296), +_c(U+1D497, "𝒗", f09d9297), +_c(U+1D498, "𝒘", f09d9298), +_c(U+1D499, "𝒙", f09d9299), +_c(U+1D49A, "𝒚", f09d929a), +_c(U+1D49B, "𝒛", f09d929b), +_c(U+1D49C, "𝒜", f09d929c), +_c(U+1D49D, "", f09d929d), +_c(U+1D49E, "𝒞", f09d929e), +_c(U+1D49F, "𝒟", f09d929f), +_c(U+1D4A0, "", f09d92a0), +_c(U+1D4A1, "", f09d92a1), +_c(U+1D4A2, "𝒢", f09d92a2), +_c(U+1D4A3, "", f09d92a3), +_c(U+1D4A4, "", f09d92a4), +_c(U+1D4A5, "𝒥", f09d92a5), +_c(U+1D4A6, "𝒦", f09d92a6), +_c(U+1D4A7, "", f09d92a7), +_c(U+1D4A8, "", f09d92a8), +_c(U+1D4A9, "𝒩", f09d92a9), +_c(U+1D4AA, "𝒪", f09d92aa), +_c(U+1D4AB, "𝒫", f09d92ab), +_c(U+1D4AC, "𝒬", f09d92ac), +_c(U+1D4AD, "", f09d92ad), +_c(U+1D4AE, "𝒮", f09d92ae), +_c(U+1D4AF, "𝒯", f09d92af), +_c(U+1D4B0, "𝒰", f09d92b0), +_c(U+1D4B1, "𝒱", f09d92b1), +_c(U+1D4B2, "𝒲", f09d92b2), +_c(U+1D4B3, "𝒳", f09d92b3), +_c(U+1D4B4, "𝒴", f09d92b4), +_c(U+1D4B5, "𝒵", f09d92b5), +_c(U+1D4B6, "𝒶", f09d92b6), +_c(U+1D4B7, "𝒷", f09d92b7), +_c(U+1D4B8, "𝒸", f09d92b8), +_c(U+1D4B9, "𝒹", f09d92b9), +_c(U+1D4BA, "", f09d92ba), +_c(U+1D4BB, "𝒻", f09d92bb), +_c(U+1D4BC, "", f09d92bc), +_c(U+1D4BD, "𝒽", f09d92bd), +_c(U+1D4BE, "𝒾", f09d92be), +_c(U+1D4BF, "𝒿", f09d92bf), +_c(U+1D4C0, "𝓀", f09d9380), +_c(U+1D4C1, "𝓁", f09d9381), +_c(U+1D4C2, "𝓂", f09d9382), +_c(U+1D4C3, "𝓃", f09d9383), +_c(U+1D4C4, "", f09d9384), +_c(U+1D4C5, "𝓅", f09d9385), +_c(U+1D4C6, "𝓆", f09d9386), +_c(U+1D4C7, "𝓇", f09d9387), +_c(U+1D4C8, "𝓈", f09d9388), +_c(U+1D4C9, "𝓉", f09d9389), +_c(U+1D4CA, "𝓊", f09d938a), +_c(U+1D4CB, "𝓋", f09d938b), +_c(U+1D4CC, "𝓌", f09d938c), +_c(U+1D4CD, "𝓍", f09d938d), +_c(U+1D4CE, "𝓎", f09d938e), +_c(U+1D4CF, "𝓏", f09d938f), +_c(U+1D4D0, "𝓐", f09d9390), +_c(U+1D4D1, "𝓑", f09d9391), +_c(U+1D4D2, "𝓒", f09d9392), +_c(U+1D4D3, "𝓓", f09d9393), +_c(U+1D4D4, "𝓔", f09d9394), +_c(U+1D4D5, "𝓕", f09d9395), +_c(U+1D4D6, "𝓖", f09d9396), +_c(U+1D4D7, "𝓗", f09d9397), +_c(U+1D4D8, "𝓘", f09d9398), +_c(U+1D4D9, "𝓙", f09d9399), +_c(U+1D4DA, "𝓚", f09d939a), +_c(U+1D4DB, "𝓛", f09d939b), +_c(U+1D4DC, "𝓜", f09d939c), +_c(U+1D4DD, "𝓝", f09d939d), +_c(U+1D4DE, "𝓞", f09d939e), +_c(U+1D4DF, "𝓟", f09d939f), +_c(U+1D4E0, "𝓠", f09d93a0), +_c(U+1D4E1, "𝓡", f09d93a1), +_c(U+1D4E2, "𝓢", f09d93a2), +_c(U+1D4E3, "𝓣", f09d93a3), +_c(U+1D4E4, "𝓤", f09d93a4), +_c(U+1D4E5, "𝓥", f09d93a5), +_c(U+1D4E6, "𝓦", f09d93a6), +_c(U+1D4E7, "𝓧", f09d93a7), +_c(U+1D4E8, "𝓨", f09d93a8), +_c(U+1D4E9, "𝓩", f09d93a9), +_c(U+1D4EA, "𝓪", f09d93aa), +_c(U+1D4EB, "𝓫", f09d93ab), +_c(U+1D4EC, "𝓬", f09d93ac), +_c(U+1D4ED, "𝓭", f09d93ad), +_c(U+1D4EE, "𝓮", f09d93ae), +_c(U+1D4EF, "𝓯", f09d93af), +_c(U+1D4F0, "𝓰", f09d93b0), +_c(U+1D4F1, "𝓱", f09d93b1), +_c(U+1D4F2, "𝓲", f09d93b2), +_c(U+1D4F3, "𝓳", f09d93b3), +_c(U+1D4F4, "𝓴", f09d93b4), +_c(U+1D4F5, "𝓵", f09d93b5), +_c(U+1D4F6, "𝓶", f09d93b6), +_c(U+1D4F7, "𝓷", f09d93b7), +_c(U+1D4F8, "𝓸", f09d93b8), +_c(U+1D4F9, "𝓹", f09d93b9), +_c(U+1D4FA, "𝓺", f09d93ba), +_c(U+1D4FB, "𝓻", f09d93bb), +_c(U+1D4FC, "𝓼", f09d93bc), +_c(U+1D4FD, "𝓽", f09d93bd), +_c(U+1D4FE, "𝓾", f09d93be), +_c(U+1D4FF, "𝓿", f09d93bf), +_c(U+1F600, "😀", f09f9880), +_c(U+1F601, "😁", f09f9881), +_c(U+1F602, "😂", f09f9882), +_c(U+1F603, "😃", f09f9883), +_c(U+1F604, "😄", f09f9884), +_c(U+1F605, "😅", f09f9885), +_c(U+1F606, "😆", f09f9886), +_c(U+1F607, "😇", f09f9887), +_c(U+1F608, "😈", f09f9888), +_c(U+1F609, "😉", f09f9889), +_c(U+1F60A, "😊", f09f988a), +_c(U+1F60B, "😋", f09f988b), +_c(U+1F60C, "😌", f09f988c), +_c(U+1F60D, "😍", f09f988d), +_c(U+1F60E, "😎", f09f988e), +_c(U+1F60F, "😏", f09f988f), +_c(U+1F610, "😐", f09f9890), +_c(U+1F611, "😑", f09f9891), +_c(U+1F612, "😒", f09f9892), +_c(U+1F613, "😓", f09f9893), +_c(U+1F614, "😔", f09f9894), +_c(U+1F615, "😕", f09f9895), +_c(U+1F616, "😖", f09f9896), +_c(U+1F617, "😗", f09f9897), +_c(U+1F618, "😘", f09f9898), +_c(U+1F619, "😙", f09f9899), +_c(U+1F61A, "😚", f09f989a), +_c(U+1F61B, "😛", f09f989b), +_c(U+1F61C, "😜", f09f989c), +_c(U+1F61D, "😝", f09f989d), +_c(U+1F61E, "😞", f09f989e), +_c(U+1F61F, "😟", f09f989f), +_c(U+1F620, "😠", f09f98a0), +_c(U+1F621, "😡", f09f98a1), +_c(U+1F622, "😢", f09f98a2), +_c(U+1F623, "😣", f09f98a3), +_c(U+1F624, "😤", f09f98a4), +_c(U+1F625, "😥", f09f98a5), +_c(U+1F626, "😦", f09f98a6), +_c(U+1F627, "😧", f09f98a7), +_c(U+1F628, "😨", f09f98a8), +_c(U+1F629, "😩", f09f98a9), +_c(U+1F62A, "😪", f09f98aa), +_c(U+1F62B, "😫", f09f98ab), +_c(U+1F62C, "😬", f09f98ac), +_c(U+1F62D, "😭", f09f98ad), +_c(U+1F62E, "😮", f09f98ae), +_c(U+1F62F, "😯", f09f98af), +_c(U+1F630, "😰", f09f98b0), +_c(U+1F631, "😱", f09f98b1), +_c(U+1F632, "😲", f09f98b2), +_c(U+1F633, "😳", f09f98b3), +_c(U+1F634, "😴", f09f98b4), +_c(U+1F635, "😵", f09f98b5), +_c(U+1F636, "😶", f09f98b6), +_c(U+1F637, "😷", f09f98b7), +_c(U+1F638, "😸", f09f98b8), +_c(U+1F639, "😹", f09f98b9), +_c(U+1F63A, "😺", f09f98ba), +_c(U+1F63B, "😻", f09f98bb), +_c(U+1F63C, "😼", f09f98bc), +_c(U+1F63D, "😽", f09f98bd), +_c(U+1F63E, "😾", f09f98be), +_c(U+1F63F, "😿", f09f98bf), +_c(U+1F640, "🙀", f09f9980), +_c(U+1F641, "🙁", f09f9981), +_c(U+1F642, "🙂", f09f9982), +_c(U+1F643, "🙃", f09f9983), +_c(U+1F644, "🙄", f09f9984), +_c(U+1F645, "🙅", f09f9985), +_c(U+1F646, "🙆", f09f9986), +_c(U+1F647, "🙇", f09f9987), +_c(U+1F648, "🙈", f09f9988), +_c(U+1F649, "🙉", f09f9989), +_c(U+1F64A, "🙊", f09f998a), +_c(U+1F64B, "🙋", f09f998b), +_c(U+1F64C, "🙌", f09f998c), +_c(U+1F64D, "🙍", f09f998d), +_c(U+1F64E, "🙎", f09f998e), +_c(U+1F64F, "🙏", f09f998f), +_c(U+1F650, "🙐", f09f9990), +_c(U+1F651, "🙑", f09f9991), +_c(U+1F652, "🙒", f09f9992), +_c(U+1F653, "🙓", f09f9993), +_c(U+1F654, "🙔", f09f9994), +_c(U+1F655, "🙕", f09f9995), +_c(U+1F656, "🙖", f09f9996), +_c(U+1F657, "🙗", f09f9997), +_c(U+1F658, "🙘", f09f9998), +_c(U+1F659, "🙙", f09f9999), +_c(U+1F65A, "🙚", f09f999a), +_c(U+1F65B, "🙛", f09f999b), +_c(U+1F65C, "🙜", f09f999c), +_c(U+1F65D, "🙝", f09f999d), +_c(U+1F65E, "🙞", f09f999e), +_c(U+1F65F, "🙟", f09f999f), +_c(U+1F660, "🙠", f09f99a0), +_c(U+1F661, "🙡", f09f99a1), +_c(U+1F662, "🙢", f09f99a2), +_c(U+1F663, "🙣", f09f99a3), +_c(U+1F664, "🙤", f09f99a4), +_c(U+1F665, "🙥", f09f99a5), +_c(U+1F666, "🙦", f09f99a6), +_c(U+1F667, "🙧", f09f99a7), +_c(U+1F668, "🙨", f09f99a8), +_c(U+1F669, "🙩", f09f99a9), +_c(U+1F66A, "🙪", f09f99aa), +_c(U+1F66B, "🙫", f09f99ab), +_c(U+1F66C, "🙬", f09f99ac), +_c(U+1F66D, "🙭", f09f99ad), +_c(U+1F66E, "🙮", f09f99ae), +_c(U+1F66F, "🙯", f09f99af), +_c(U+1F670, "🙰", f09f99b0), +_c(U+1F671, "🙱", f09f99b1), +_c(U+1F672, "🙲", f09f99b2), +_c(U+1F673, "🙳", f09f99b3), +_c(U+1F674, "🙴", f09f99b4), +_c(U+1F675, "🙵", f09f99b5), +_c(U+1F676, "🙶", f09f99b6), +_c(U+1F677, "🙷", f09f99b7), +_c(U+1F678, "🙸", f09f99b8), +_c(U+1F679, "🙹", f09f99b9), +_c(U+1F67A, "🙺", f09f99ba), +_c(U+1F67B, "🙻", f09f99bb), +_c(U+1F67C, "🙼", f09f99bc), +_c(U+1F67D, "🙽", f09f99bd), +_c(U+1F67E, "🙾", f09f99be), +_c(U+1F67F, "🙿", f09f99bf), +_c(U+1F680, "🚀", f09f9a80), +_c(U+1F681, "🚁", f09f9a81), +_c(U+1F682, "🚂", f09f9a82), +_c(U+1F683, "🚃", f09f9a83), +_c(U+1F684, "🚄", f09f9a84), +_c(U+1F685, "🚅", f09f9a85), +_c(U+1F686, "🚆", f09f9a86), +_c(U+1F687, "🚇", f09f9a87), +_c(U+1F688, "🚈", f09f9a88), +_c(U+1F689, "🚉", f09f9a89), +_c(U+1F68A, "🚊", f09f9a8a), +_c(U+1F68B, "🚋", f09f9a8b), +_c(U+1F68C, "🚌", f09f9a8c), +_c(U+1F68D, "🚍", f09f9a8d), +_c(U+1F68E, "🚎", f09f9a8e), +_c(U+1F68F, "🚏", f09f9a8f), +_c(U+1F690, "🚐", f09f9a90), +_c(U+1F691, "🚑", f09f9a91), +_c(U+1F692, "🚒", f09f9a92), +_c(U+1F693, "🚓", f09f9a93), +_c(U+1F694, "🚔", f09f9a94), +_c(U+1F695, "🚕", f09f9a95), +_c(U+1F696, "🚖", f09f9a96), +_c(U+1F697, "🚗", f09f9a97), +_c(U+1F698, "🚘", f09f9a98), +_c(U+1F699, "🚙", f09f9a99), +_c(U+1F69A, "🚚", f09f9a9a), +_c(U+1F69B, "🚛", f09f9a9b), +_c(U+1F69C, "🚜", f09f9a9c), +_c(U+1F69D, "🚝", f09f9a9d), +_c(U+1F69E, "🚞", f09f9a9e), +_c(U+1F69F, "🚟", f09f9a9f), +_c(U+1F6A0, "🚠", f09f9aa0), +_c(U+1F6A1, "🚡", f09f9aa1), +_c(U+1F6A2, "🚢", f09f9aa2), +_c(U+1F6A3, "🚣", f09f9aa3), +_c(U+1F6A4, "🚤", f09f9aa4), +_c(U+1F6A5, "🚥", f09f9aa5), +_c(U+1F6A6, "🚦", f09f9aa6), +_c(U+1F6A7, "🚧", f09f9aa7), +_c(U+1F6A8, "🚨", f09f9aa8), +_c(U+1F6A9, "🚩", f09f9aa9), +_c(U+1F6AA, "🚪", f09f9aaa), +_c(U+1F6AB, "🚫", f09f9aab), +_c(U+1F6AC, "🚬", f09f9aac), +_c(U+1F6AD, "🚭", f09f9aad), +_c(U+1F6AE, "🚮", f09f9aae), +_c(U+1F6AF, "🚯", f09f9aaf), +_c(U+1F6B0, "🚰", f09f9ab0), +_c(U+1F6B1, "🚱", f09f9ab1), +_c(U+1F6B2, "🚲", f09f9ab2), +_c(U+1F6B3, "🚳", f09f9ab3), +_c(U+1F6B4, "🚴", f09f9ab4), +_c(U+1F6B5, "🚵", f09f9ab5), +_c(U+1F6B6, "🚶", f09f9ab6), +_c(U+1F6B7, "🚷", f09f9ab7), +_c(U+1F6B8, "🚸", f09f9ab8), +_c(U+1F6B9, "🚹", f09f9ab9), +_c(U+1F6BA, "🚺", f09f9aba), +_c(U+1F6BB, "🚻", f09f9abb), +_c(U+1F6BC, "🚼", f09f9abc), +_c(U+1F6BD, "🚽", f09f9abd), +_c(U+1F6BE, "🚾", f09f9abe), +_c(U+1F6BF, "🚿", f09f9abf), +_c(U+1F6C0, "🛀", f09f9b80), +_c(U+1F6C1, "🛁", f09f9b81), +_c(U+1F6C2, "🛂", f09f9b82), +_c(U+1F6C3, "🛃", f09f9b83), +_c(U+1F6C4, "🛄", f09f9b84), +_c(U+1F6C5, "🛅", f09f9b85), +_c(U+1F6C6, "🛆", f09f9b86), +_c(U+1F6C7, "🛇", f09f9b87), +_c(U+1F6C8, "🛈", f09f9b88), +_c(U+1F6C9, "🛉", f09f9b89), +_c(U+1F6CA, "🛊", f09f9b8a), +_c(U+1F6CB, "🛋", f09f9b8b), +_c(U+1F6CC, "🛌", f09f9b8c), +_c(U+1F6CD, "🛍", f09f9b8d), +_c(U+1F6CE, "🛎", f09f9b8e), +_c(U+1F6CF, "🛏", f09f9b8f), +_c(U+1F6D0, "🛐", f09f9b90), +_c(U+1F6D1, "🛑", f09f9b91), +_c(U+1F6D2, "🛒", f09f9b92), +_c(U+1F6D3, "🛓", f09f9b93), +_c(U+1F6D4, "🛔", f09f9b94), +_c(U+1F6D5, "🛕", f09f9b95), +_c(U+1F6D6, "🛖", f09f9b96), +_c(U+1F6D7, "🛗", f09f9b97), +_c(U+1F6D8, "", f09f9b98), +_c(U+1F6D9, "", f09f9b99), +_c(U+1F6DA, "", f09f9b9a), +_c(U+1F6DB, "", f09f9b9b), +_c(U+1F6DC, "🛜", f09f9b9c), +_c(U+1F6DD, "🛝", f09f9b9d), +_c(U+1F6DE, "🛞", f09f9b9e), +_c(U+1F6DF, "🛟", f09f9b9f), +_c(U+1F6E0, "🛠", f09f9ba0), +_c(U+1F6E1, "🛡", f09f9ba1), +_c(U+1F6E2, "🛢", f09f9ba2), +_c(U+1F6E3, "🛣", f09f9ba3), +_c(U+1F6E4, "🛤", f09f9ba4), +_c(U+1F6E5, "🛥", f09f9ba5), +_c(U+1F6E6, "🛦", f09f9ba6), +_c(U+1F6E7, "🛧", f09f9ba7), +_c(U+1F6E8, "🛨", f09f9ba8), +_c(U+1F6E9, "🛩", f09f9ba9), +_c(U+1F6EA, "🛪", f09f9baa), +_c(U+1F6EB, "🛫", f09f9bab), +_c(U+1F6EC, "🛬", f09f9bac), +_c(U+1F6ED, "", f09f9bad), +_c(U+1F6EE, "", f09f9bae), +_c(U+1F6EF, "", f09f9baf), +_c(U+1F6F0, "🛰", f09f9bb0), +_c(U+1F6F1, "🛱", f09f9bb1), +_c(U+1F6F2, "🛲", f09f9bb2), +_c(U+1F6F3, "🛳", f09f9bb3), +_c(U+1F6F4, "🛴", f09f9bb4), +_c(U+1F6F5, "🛵", f09f9bb5), +_c(U+1F6F6, "🛶", f09f9bb6), +_c(U+1F6F7, "🛷", f09f9bb7), +_c(U+1F6F8, "🛸", f09f9bb8), +_c(U+1F6F9, "🛹", f09f9bb9), +_c(U+1F6FA, "🛺", f09f9bba), +_c(U+1F6FB, "🛻", f09f9bbb), +_c(U+1F6FC, "🛼", f09f9bbc), +_c(U+1F6FD, "", f09f9bbd), +_c(U+1F6FE, "", f09f9bbe), +_c(U+1F6FF, "", f09f9bbf), +_c(U+1F700, "🜀", f09f9c80), +_c(U+1F701, "🜁", f09f9c81), +_c(U+1F702, "🜂", f09f9c82), +_c(U+1F703, "🜃", f09f9c83), +_c(U+1F704, "🜄", f09f9c84), +_c(U+1F705, "🜅", f09f9c85), +_c(U+1F706, "🜆", f09f9c86), +_c(U+1F707, "🜇", f09f9c87), +_c(U+1F708, "🜈", f09f9c88), +_c(U+1F709, "🜉", f09f9c89), +_c(U+1F70A, "🜊", f09f9c8a), +_c(U+1F70B, "🜋", f09f9c8b), +_c(U+1F70C, "🜌", f09f9c8c), +_c(U+1F70D, "🜍", f09f9c8d), +_c(U+1F70E, "🜎", f09f9c8e), +_c(U+1F70F, "🜏", f09f9c8f), +_c(U+1F710, "🜐", f09f9c90), +_c(U+1F711, "🜑", f09f9c91), +_c(U+1F712, "🜒", f09f9c92), +_c(U+1F713, "🜓", f09f9c93), +_c(U+1F714, "🜔", f09f9c94), +_c(U+1F715, "🜕", f09f9c95), +_c(U+1F716, "🜖", f09f9c96), +_c(U+1F717, "🜗", f09f9c97), +_c(U+1F718, "🜘", f09f9c98), +_c(U+1F719, "🜙", f09f9c99), +_c(U+1F71A, "🜚", f09f9c9a), +_c(U+1F71B, "🜛", f09f9c9b), +_c(U+1F71C, "🜜", f09f9c9c), +_c(U+1F71D, "🜝", f09f9c9d), +_c(U+1F71E, "🜞", f09f9c9e), +_c(U+1F71F, "🜟", f09f9c9f), +_c(U+1F720, "🜠", f09f9ca0), +_c(U+1F721, "🜡", f09f9ca1), +_c(U+1F722, "🜢", f09f9ca2), +_c(U+1F723, "🜣", f09f9ca3), +_c(U+1F724, "🜤", f09f9ca4), +_c(U+1F725, "🜥", f09f9ca5), +_c(U+1F726, "🜦", f09f9ca6), +_c(U+1F727, "🜧", f09f9ca7), +_c(U+1F728, "🜨", f09f9ca8), +_c(U+1F729, "🜩", f09f9ca9), +_c(U+1F72A, "🜪", f09f9caa), +_c(U+1F72B, "🜫", f09f9cab), +_c(U+1F72C, "🜬", f09f9cac), +_c(U+1F72D, "🜭", f09f9cad), +_c(U+1F72E, "🜮", f09f9cae), +_c(U+1F72F, "🜯", f09f9caf), +_c(U+1F730, "🜰", f09f9cb0), +_c(U+1F731, "🜱", f09f9cb1), +_c(U+1F732, "🜲", f09f9cb2), +_c(U+1F733, "🜳", f09f9cb3), +_c(U+1F734, "🜴", f09f9cb4), +_c(U+1F735, "🜵", f09f9cb5), +_c(U+1F736, "🜶", f09f9cb6), +_c(U+1F737, "🜷", f09f9cb7), +_c(U+1F738, "🜸", f09f9cb8), +_c(U+1F739, "🜹", f09f9cb9), +_c(U+1F73A, "🜺", f09f9cba), +_c(U+1F73B, "🜻", f09f9cbb), +_c(U+1F73C, "🜼", f09f9cbc), +_c(U+1F73D, "🜽", f09f9cbd), +_c(U+1F73E, "🜾", f09f9cbe), +_c(U+1F73F, "🜿", f09f9cbf), +_c(U+1F740, "🝀", f09f9d80), +_c(U+1F741, "🝁", f09f9d81), +_c(U+1F742, "🝂", f09f9d82), +_c(U+1F743, "🝃", f09f9d83), +_c(U+1F744, "🝄", f09f9d84), +_c(U+1F745, "🝅", f09f9d85), +_c(U+1F746, "🝆", f09f9d86), +_c(U+1F747, "🝇", f09f9d87), +_c(U+1F748, "🝈", f09f9d88), +_c(U+1F749, "🝉", f09f9d89), +_c(U+1F74A, "🝊", f09f9d8a), +_c(U+1F74B, "🝋", f09f9d8b), +_c(U+1F74C, "🝌", f09f9d8c), +_c(U+1F74D, "🝍", f09f9d8d), +_c(U+1F74E, "🝎", f09f9d8e), +_c(U+1F74F, "🝏", f09f9d8f), +_c(U+1F750, "🝐", f09f9d90), +_c(U+1F751, "🝑", f09f9d91), +_c(U+1F752, "🝒", f09f9d92), +_c(U+1F753, "🝓", f09f9d93), +_c(U+1F754, "🝔", f09f9d94), +_c(U+1F755, "🝕", f09f9d95), +_c(U+1F756, "🝖", f09f9d96), +_c(U+1F757, "🝗", f09f9d97), +_c(U+1F758, "🝘", f09f9d98), +_c(U+1F759, "🝙", f09f9d99), +_c(U+1F75A, "🝚", f09f9d9a), +_c(U+1F75B, "🝛", f09f9d9b), +_c(U+1F75C, "🝜", f09f9d9c), +_c(U+1F75D, "🝝", f09f9d9d), +_c(U+1F75E, "🝞", f09f9d9e), +_c(U+1F75F, "🝟", f09f9d9f), +_c(U+1F760, "🝠", f09f9da0), +_c(U+1F761, "🝡", f09f9da1), +_c(U+1F762, "🝢", f09f9da2), +_c(U+1F763, "🝣", f09f9da3), +_c(U+1F764, "🝤", f09f9da4), +_c(U+1F765, "🝥", f09f9da5), +_c(U+1F766, "🝦", f09f9da6), +_c(U+1F767, "🝧", f09f9da7), +_c(U+1F768, "🝨", f09f9da8), +_c(U+1F769, "🝩", f09f9da9), +_c(U+1F76A, "🝪", f09f9daa), +_c(U+1F76B, "🝫", f09f9dab), +_c(U+1F76C, "🝬", f09f9dac), +_c(U+1F76D, "🝭", f09f9dad), +_c(U+1F76E, "🝮", f09f9dae), +_c(U+1F76F, "🝯", f09f9daf), +_c(U+1F770, "🝰", f09f9db0), +_c(U+1F771, "🝱", f09f9db1), +_c(U+1F772, "🝲", f09f9db2), +_c(U+1F773, "🝳", f09f9db3), +_c(U+1F774, "🝴", f09f9db4), +_c(U+1F775, "🝵", f09f9db5), +_c(U+1F776, "🝶", f09f9db6), +_c(U+1F777, "", f09f9db7), +_c(U+1F778, "", f09f9db8), +_c(U+1F779, "", f09f9db9), +_c(U+1F77A, "", f09f9dba), +_c(U+1F77B, "🝻", f09f9dbb), +_c(U+1F77C, "🝼", f09f9dbc), +_c(U+1F77D, "🝽", f09f9dbd), +_c(U+1F77E, "🝾", f09f9dbe), +_c(U+1F77F, "🝿", f09f9dbf), +_c(U+1F780, "🞀", f09f9e80), +_c(U+1F781, "🞁", f09f9e81), +_c(U+1F782, "🞂", f09f9e82), +_c(U+1F783, "🞃", f09f9e83), +_c(U+1F784, "🞄", f09f9e84), +_c(U+1F785, "🞅", f09f9e85), +_c(U+1F786, "🞆", f09f9e86), +_c(U+1F787, "🞇", f09f9e87), +_c(U+1F788, "🞈", f09f9e88), +_c(U+1F789, "🞉", f09f9e89), +_c(U+1F78A, "🞊", f09f9e8a), +_c(U+1F78B, "🞋", f09f9e8b), +_c(U+1F78C, "🞌", f09f9e8c), +_c(U+1F78D, "🞍", f09f9e8d), +_c(U+1F78E, "🞎", f09f9e8e), +_c(U+1F78F, "🞏", f09f9e8f), +_c(U+1F790, "🞐", f09f9e90), +_c(U+1F791, "🞑", f09f9e91), +_c(U+1F792, "🞒", f09f9e92), +_c(U+1F793, "🞓", f09f9e93), +_c(U+1F794, "🞔", f09f9e94), +_c(U+1F795, "🞕", f09f9e95), +_c(U+1F796, "🞖", f09f9e96), +_c(U+1F797, "🞗", f09f9e97), +_c(U+1F798, "🞘", f09f9e98), +_c(U+1F799, "🞙", f09f9e99), +_c(U+1F79A, "🞚", f09f9e9a), +_c(U+1F79B, "🞛", f09f9e9b), +_c(U+1F79C, "🞜", f09f9e9c), +_c(U+1F79D, "🞝", f09f9e9d), +_c(U+1F79E, "🞞", f09f9e9e), +_c(U+1F79F, "🞟", f09f9e9f), +_c(U+1F7A0, "🞠", f09f9ea0), +_c(U+1F7A1, "🞡", f09f9ea1), +_c(U+1F7A2, "🞢", f09f9ea2), +_c(U+1F7A3, "🞣", f09f9ea3), +_c(U+1F7A4, "🞤", f09f9ea4), +_c(U+1F7A5, "🞥", f09f9ea5), +_c(U+1F7A6, "🞦", f09f9ea6), +_c(U+1F7A7, "🞧", f09f9ea7), +_c(U+1F7A8, "🞨", f09f9ea8), +_c(U+1F7A9, "🞩", f09f9ea9), +_c(U+1F7AA, "🞪", f09f9eaa), +_c(U+1F7AB, "🞫", f09f9eab), +_c(U+1F7AC, "🞬", f09f9eac), +_c(U+1F7AD, "🞭", f09f9ead), +_c(U+1F7AE, "🞮", f09f9eae), +_c(U+1F7AF, "🞯", f09f9eaf), +_c(U+1F7B0, "🞰", f09f9eb0), +_c(U+1F7B1, "🞱", f09f9eb1), +_c(U+1F7B2, "🞲", f09f9eb2), +_c(U+1F7B3, "🞳", f09f9eb3), +_c(U+1F7B4, "🞴", f09f9eb4), +_c(U+1F7B5, "🞵", f09f9eb5), +_c(U+1F7B6, "🞶", f09f9eb6), +_c(U+1F7B7, "🞷", f09f9eb7), +_c(U+1F7B8, "🞸", f09f9eb8), +_c(U+1F7B9, "🞹", f09f9eb9), +_c(U+1F7BA, "🞺", f09f9eba), +_c(U+1F7BB, "🞻", f09f9ebb), +_c(U+1F7BC, "🞼", f09f9ebc), +_c(U+1F7BD, "🞽", f09f9ebd), +_c(U+1F7BE, "🞾", f09f9ebe), +_c(U+1F7BF, "🞿", f09f9ebf), +_c(U+1F7C0, "🟀", f09f9f80), +_c(U+1F7C1, "🟁", f09f9f81), +_c(U+1F7C2, "🟂", f09f9f82), +_c(U+1F7C3, "🟃", f09f9f83), +_c(U+1F7C4, "🟄", f09f9f84), +_c(U+1F7C5, "🟅", f09f9f85), +_c(U+1F7C6, "🟆", f09f9f86), +_c(U+1F7C7, "🟇", f09f9f87), +_c(U+1F7C8, "🟈", f09f9f88), +_c(U+1F7C9, "🟉", f09f9f89), +_c(U+1F7CA, "🟊", f09f9f8a), +_c(U+1F7CB, "🟋", f09f9f8b), +_c(U+1F7CC, "🟌", f09f9f8c), +_c(U+1F7CD, "🟍", f09f9f8d), +_c(U+1F7CE, "🟎", f09f9f8e), +_c(U+1F7CF, "🟏", f09f9f8f), +_c(U+1F7D0, "🟐", f09f9f90), +_c(U+1F7D1, "🟑", f09f9f91), +_c(U+1F7D2, "🟒", f09f9f92), +_c(U+1F7D3, "🟓", f09f9f93), +_c(U+1F7D4, "🟔", f09f9f94), +_c(U+1F7D5, "🟕", f09f9f95), +_c(U+1F7D6, "🟖", f09f9f96), +_c(U+1F7D7, "🟗", f09f9f97), +_c(U+1F7D8, "🟘", f09f9f98), +_c(U+1F7D9, "🟙", f09f9f99), +_c(U+1F7DA, "", f09f9f9a), +_c(U+1F7DB, "", f09f9f9b), +_c(U+1F7DC, "", f09f9f9c), +_c(U+1F7DD, "", f09f9f9d), +_c(U+1F7DE, "", f09f9f9e), +_c(U+1F7DF, "", f09f9f9f), +_c(U+1F7E0, "🟠", f09f9fa0), +_c(U+1F7E1, "🟡", f09f9fa1), +_c(U+1F7E2, "🟢", f09f9fa2), +_c(U+1F7E3, "🟣", f09f9fa3), +_c(U+1F7E4, "🟤", f09f9fa4), +_c(U+1F7E5, "🟥", f09f9fa5), +_c(U+1F7E6, "🟦", f09f9fa6), +_c(U+1F7E7, "🟧", f09f9fa7), +_c(U+1F7E8, "🟨", f09f9fa8), +_c(U+1F7E9, "🟩", f09f9fa9), +_c(U+1F7EA, "🟪", f09f9faa), +_c(U+1F7EB, "🟫", f09f9fab), +_c(U+1F7EC, "", f09f9fac), +_c(U+1F7ED, "", f09f9fad), +_c(U+1F7EE, "", f09f9fae), +_c(U+1F7EF, "", f09f9faf), +_c(U+1F7F0, "🟰", f09f9fb0), +_c(U+1F7F1, "", f09f9fb1), +_c(U+1F7F2, "", f09f9fb2), +_c(U+1F7F3, "", f09f9fb3), +_c(U+1F7F4, "", f09f9fb4), +_c(U+1F7F5, "", f09f9fb5), +_c(U+1F7F6, "", f09f9fb6), +_c(U+1F7F7, "", f09f9fb7), +_c(U+1F7F8, "", f09f9fb8), +_c(U+1F7F9, "", f09f9fb9), +_c(U+1F7FA, "", f09f9fba), +_c(U+1F7FB, "", f09f9fbb), +_c(U+1F7FC, "", f09f9fbc), +_c(U+1F7FD, "", f09f9fbd), +_c(U+1F7FE, "", f09f9fbe), +_c(U+1F7FF, "", f09f9fbf), +_c(U+1F800, "🠀", f09fa080), +_c(U+1F801, "🠁", f09fa081), +_c(U+1F802, "🠂", f09fa082), +_c(U+1F803, "🠃", f09fa083), +_c(U+1F804, "🠄", f09fa084), +_c(U+1F805, "🠅", f09fa085), +_c(U+1F806, "🠆", f09fa086), +_c(U+1F807, "🠇", f09fa087), +_c(U+1F808, "🠈", f09fa088), +_c(U+1F809, "🠉", f09fa089), +_c(U+1F80A, "🠊", f09fa08a), +_c(U+1F80B, "🠋", f09fa08b), +_c(U+1F80C, "", f09fa08c), +_c(U+1F80D, "", f09fa08d), +_c(U+1F80E, "", f09fa08e), +_c(U+1F80F, "", f09fa08f), +_c(U+1F810, "🠐", f09fa090), +_c(U+1F811, "🠑", f09fa091), +_c(U+1F812, "🠒", f09fa092), +_c(U+1F813, "🠓", f09fa093), +_c(U+1F814, "🠔", f09fa094), +_c(U+1F815, "🠕", f09fa095), +_c(U+1F816, "🠖", f09fa096), +_c(U+1F817, "🠗", f09fa097), +_c(U+1F818, "🠘", f09fa098), +_c(U+1F819, "🠙", f09fa099), +_c(U+1F81A, "🠚", f09fa09a), +_c(U+1F81B, "🠛", f09fa09b), +_c(U+1F81C, "🠜", f09fa09c), +_c(U+1F81D, "🠝", f09fa09d), +_c(U+1F81E, "🠞", f09fa09e), +_c(U+1F81F, "🠟", f09fa09f), +_c(U+1F820, "🠠", f09fa0a0), +_c(U+1F821, "🠡", f09fa0a1), +_c(U+1F822, "🠢", f09fa0a2), +_c(U+1F823, "🠣", f09fa0a3), +_c(U+1F824, "🠤", f09fa0a4), +_c(U+1F825, "🠥", f09fa0a5), +_c(U+1F826, "🠦", f09fa0a6), +_c(U+1F827, "🠧", f09fa0a7), +_c(U+1F828, "🠨", f09fa0a8), +_c(U+1F829, "🠩", f09fa0a9), +_c(U+1F82A, "🠪", f09fa0aa), +_c(U+1F82B, "🠫", f09fa0ab), +_c(U+1F82C, "🠬", f09fa0ac), +_c(U+1F82D, "🠭", f09fa0ad), +_c(U+1F82E, "🠮", f09fa0ae), +_c(U+1F82F, "🠯", f09fa0af), +_c(U+1F830, "🠰", f09fa0b0), +_c(U+1F831, "🠱", f09fa0b1), +_c(U+1F832, "🠲", f09fa0b2), +_c(U+1F833, "🠳", f09fa0b3), +_c(U+1F834, "🠴", f09fa0b4), +_c(U+1F835, "🠵", f09fa0b5), +_c(U+1F836, "🠶", f09fa0b6), +_c(U+1F837, "🠷", f09fa0b7), +_c(U+1F838, "🠸", f09fa0b8), +_c(U+1F839, "🠹", f09fa0b9), +_c(U+1F83A, "🠺", f09fa0ba), +_c(U+1F83B, "🠻", f09fa0bb), +_c(U+1F83C, "🠼", f09fa0bc), +_c(U+1F83D, "🠽", f09fa0bd), +_c(U+1F83E, "🠾", f09fa0be), +_c(U+1F83F, "🠿", f09fa0bf), +_c(U+1F840, "🡀", f09fa180), +_c(U+1F841, "🡁", f09fa181), +_c(U+1F842, "🡂", f09fa182), +_c(U+1F843, "🡃", f09fa183), +_c(U+1F844, "🡄", f09fa184), +_c(U+1F845, "🡅", f09fa185), +_c(U+1F846, "🡆", f09fa186), +_c(U+1F847, "🡇", f09fa187), +_c(U+1F848, "", f09fa188), +_c(U+1F849, "", f09fa189), +_c(U+1F84A, "", f09fa18a), +_c(U+1F84B, "", f09fa18b), +_c(U+1F84C, "", f09fa18c), +_c(U+1F84D, "", f09fa18d), +_c(U+1F84E, "", f09fa18e), +_c(U+1F84F, "", f09fa18f), +_c(U+1F850, "🡐", f09fa190), +_c(U+1F851, "🡑", f09fa191), +_c(U+1F852, "🡒", f09fa192), +_c(U+1F853, "🡓", f09fa193), +_c(U+1F854, "🡔", f09fa194), +_c(U+1F855, "🡕", f09fa195), +_c(U+1F856, "🡖", f09fa196), +_c(U+1F857, "🡗", f09fa197), +_c(U+1F858, "🡘", f09fa198), +_c(U+1F859, "🡙", f09fa199), +_c(U+1F85A, "", f09fa19a), +_c(U+1F85B, "", f09fa19b), +_c(U+1F85C, "", f09fa19c), +_c(U+1F85D, "", f09fa19d), +_c(U+1F85E, "", f09fa19e), +_c(U+1F85F, "", f09fa19f), +_c(U+1F860, "🡠", f09fa1a0), +_c(U+1F861, "🡡", f09fa1a1), +_c(U+1F862, "🡢", f09fa1a2), +_c(U+1F863, "🡣", f09fa1a3), +_c(U+1F864, "🡤", f09fa1a4), +_c(U+1F865, "🡥", f09fa1a5), +_c(U+1F866, "🡦", f09fa1a6), +_c(U+1F867, "🡧", f09fa1a7), +_c(U+1F868, "🡨", f09fa1a8), +_c(U+1F869, "🡩", f09fa1a9), +_c(U+1F86A, "🡪", f09fa1aa), +_c(U+1F86B, "🡫", f09fa1ab), +_c(U+1F86C, "🡬", f09fa1ac), +_c(U+1F86D, "🡭", f09fa1ad), +_c(U+1F86E, "🡮", f09fa1ae), +_c(U+1F86F, "🡯", f09fa1af), +_c(U+1F870, "🡰", f09fa1b0), +_c(U+1F871, "🡱", f09fa1b1), +_c(U+1F872, "🡲", f09fa1b2), +_c(U+1F873, "🡳", f09fa1b3), +_c(U+1F874, "🡴", f09fa1b4), +_c(U+1F875, "🡵", f09fa1b5), +_c(U+1F876, "🡶", f09fa1b6), +_c(U+1F877, "🡷", f09fa1b7), +_c(U+1F878, "🡸", f09fa1b8), +_c(U+1F879, "🡹", f09fa1b9), +_c(U+1F87A, "🡺", f09fa1ba), +_c(U+1F87B, "🡻", f09fa1bb), +_c(U+1F87C, "🡼", f09fa1bc), +_c(U+1F87D, "🡽", f09fa1bd), +_c(U+1F87E, "🡾", f09fa1be), +_c(U+1F87F, "🡿", f09fa1bf), +_c(U+1F880, "🢀", f09fa280), +_c(U+1F881, "🢁", f09fa281), +_c(U+1F882, "🢂", f09fa282), +_c(U+1F883, "🢃", f09fa283), +_c(U+1F884, "🢄", f09fa284), +_c(U+1F885, "🢅", f09fa285), +_c(U+1F886, "🢆", f09fa286), +_c(U+1F887, "🢇", f09fa287), +_c(U+1F888, "", f09fa288), +_c(U+1F889, "", f09fa289), +_c(U+1F88A, "", f09fa28a), +_c(U+1F88B, "", f09fa28b), +_c(U+1F88C, "", f09fa28c), +_c(U+1F88D, "", f09fa28d), +_c(U+1F88E, "", f09fa28e), +_c(U+1F88F, "", f09fa28f), +_c(U+1F890, "🢐", f09fa290), +_c(U+1F891, "🢑", f09fa291), +_c(U+1F892, "🢒", f09fa292), +_c(U+1F893, "🢓", f09fa293), +_c(U+1F894, "🢔", f09fa294), +_c(U+1F895, "🢕", f09fa295), +_c(U+1F896, "🢖", f09fa296), +_c(U+1F897, "🢗", f09fa297), +_c(U+1F898, "🢘", f09fa298), +_c(U+1F899, "🢙", f09fa299), +_c(U+1F89A, "🢚", f09fa29a), +_c(U+1F89B, "🢛", f09fa29b), +_c(U+1F89C, "🢜", f09fa29c), +_c(U+1F89D, "🢝", f09fa29d), +_c(U+1F89E, "🢞", f09fa29e), +_c(U+1F89F, "🢟", f09fa29f), +_c(U+1F8A0, "🢠", f09fa2a0), +_c(U+1F8A1, "🢡", f09fa2a1), +_c(U+1F8A2, "🢢", f09fa2a2), +_c(U+1F8A3, "🢣", f09fa2a3), +_c(U+1F8A4, "🢤", f09fa2a4), +_c(U+1F8A5, "🢥", f09fa2a5), +_c(U+1F8A6, "🢦", f09fa2a6), +_c(U+1F8A7, "🢧", f09fa2a7), +_c(U+1F8A8, "🢨", f09fa2a8), +_c(U+1F8A9, "🢩", f09fa2a9), +_c(U+1F8AA, "🢪", f09fa2aa), +_c(U+1F8AB, "🢫", f09fa2ab), +_c(U+1F8AC, "🢬", f09fa2ac), +_c(U+1F8AD, "🢭", f09fa2ad), +_c(U+1F8AE, "", f09fa2ae), +_c(U+1F8AF, "", f09fa2af), +_c(U+1F8B0, "🢰", f09fa2b0), +_c(U+1F8B1, "🢱", f09fa2b1), +_c(U+1F8B2, "", f09fa2b2), +_c(U+1F8B3, "", f09fa2b3), +_c(U+1F8B4, "", f09fa2b4), +_c(U+1F8B5, "", f09fa2b5), +_c(U+1F8B6, "", f09fa2b6), +_c(U+1F8B7, "", f09fa2b7), +_c(U+1F8B8, "", f09fa2b8), +_c(U+1F8B9, "", f09fa2b9), +_c(U+1F8BA, "", f09fa2ba), +_c(U+1F8BB, "", f09fa2bb), +_c(U+1F8BC, "", f09fa2bc), +_c(U+1F8BD, "", f09fa2bd), +_c(U+1F8BE, "", f09fa2be), +_c(U+1F8BF, "", f09fa2bf), +_c(U+1F8C0, "", f09fa380), +_c(U+1F8C1, "", f09fa381), +_c(U+1F8C2, "", f09fa382), +_c(U+1F8C3, "", f09fa383), +_c(U+1F8C4, "", f09fa384), +_c(U+1F8C5, "", f09fa385), +_c(U+1F8C6, "", f09fa386), +_c(U+1F8C7, "", f09fa387), +_c(U+1F8C8, "", f09fa388), +_c(U+1F8C9, "", f09fa389), +_c(U+1F8CA, "", f09fa38a), +_c(U+1F8CB, "", f09fa38b), +_c(U+1F8CC, "", f09fa38c), +_c(U+1F8CD, "", f09fa38d), +_c(U+1F8CE, "", f09fa38e), +_c(U+1F8CF, "", f09fa38f), +_c(U+1F8D0, "", f09fa390), +_c(U+1F8D1, "", f09fa391), +_c(U+1F8D2, "", f09fa392), +_c(U+1F8D3, "", f09fa393), +_c(U+1F8D4, "", f09fa394), +_c(U+1F8D5, "", f09fa395), +_c(U+1F8D6, "", f09fa396), +_c(U+1F8D7, "", f09fa397), +_c(U+1F8D8, "", f09fa398), +_c(U+1F8D9, "", f09fa399), +_c(U+1F8DA, "", f09fa39a), +_c(U+1F8DB, "", f09fa39b), +_c(U+1F8DC, "", f09fa39c), +_c(U+1F8DD, "", f09fa39d), +_c(U+1F8DE, "", f09fa39e), +_c(U+1F8DF, "", f09fa39f), +_c(U+1F8E0, "", f09fa3a0), +_c(U+1F8E1, "", f09fa3a1), +_c(U+1F8E2, "", f09fa3a2), +_c(U+1F8E3, "", f09fa3a3), +_c(U+1F8E4, "", f09fa3a4), +_c(U+1F8E5, "", f09fa3a5), +_c(U+1F8E6, "", f09fa3a6), +_c(U+1F8E7, "", f09fa3a7), +_c(U+1F8E8, "", f09fa3a8), +_c(U+1F8E9, "", f09fa3a9), +_c(U+1F8EA, "", f09fa3aa), +_c(U+1F8EB, "", f09fa3ab), +_c(U+1F8EC, "", f09fa3ac), +_c(U+1F8ED, "", f09fa3ad), +_c(U+1F8EE, "", f09fa3ae), +_c(U+1F8EF, "", f09fa3af), +_c(U+1F8F0, "", f09fa3b0), +_c(U+1F8F1, "", f09fa3b1), +_c(U+1F8F2, "", f09fa3b2), +_c(U+1F8F3, "", f09fa3b3), +_c(U+1F8F4, "", f09fa3b4), +_c(U+1F8F5, "", f09fa3b5), +_c(U+1F8F6, "", f09fa3b6), +_c(U+1F8F7, "", f09fa3b7), +_c(U+1F8F8, "", f09fa3b8), +_c(U+1F8F9, "", f09fa3b9), +_c(U+1F8FA, "", f09fa3ba), +_c(U+1F8FB, "", f09fa3bb), +_c(U+1F8FC, "", f09fa3bc), +_c(U+1F8FD, "", f09fa3bd), +_c(U+1F8FE, "", f09fa3be), +_c(U+1F8FF, "", f09fa3bf), +_c(U+1F900, "🤀", f09fa480), +_c(U+1F901, "🤁", f09fa481), +_c(U+1F902, "🤂", f09fa482), +_c(U+1F903, "🤃", f09fa483), +_c(U+1F904, "🤄", f09fa484), +_c(U+1F905, "🤅", f09fa485), +_c(U+1F906, "🤆", f09fa486), +_c(U+1F907, "🤇", f09fa487), +_c(U+1F908, "🤈", f09fa488), +_c(U+1F909, "🤉", f09fa489), +_c(U+1F90A, "🤊", f09fa48a), +_c(U+1F90B, "🤋", f09fa48b), +_c(U+1F90C, "🤌", f09fa48c), +_c(U+1F90D, "🤍", f09fa48d), +_c(U+1F90E, "🤎", f09fa48e), +_c(U+1F90F, "🤏", f09fa48f), +_c(U+1F910, "🤐", f09fa490), +_c(U+1F911, "🤑", f09fa491), +_c(U+1F912, "🤒", f09fa492), +_c(U+1F913, "🤓", f09fa493), +_c(U+1F914, "🤔", f09fa494), +_c(U+1F915, "🤕", f09fa495), +_c(U+1F916, "🤖", f09fa496), +_c(U+1F917, "🤗", f09fa497), +_c(U+1F918, "🤘", f09fa498), +_c(U+1F919, "🤙", f09fa499), +_c(U+1F91A, "🤚", f09fa49a), +_c(U+1F91B, "🤛", f09fa49b), +_c(U+1F91C, "🤜", f09fa49c), +_c(U+1F91D, "🤝", f09fa49d), +_c(U+1F91E, "🤞", f09fa49e), +_c(U+1F91F, "🤟", f09fa49f), +_c(U+1F920, "🤠", f09fa4a0), +_c(U+1F921, "🤡", f09fa4a1), +_c(U+1F922, "🤢", f09fa4a2), +_c(U+1F923, "🤣", f09fa4a3), +_c(U+1F924, "🤤", f09fa4a4), +_c(U+1F925, "🤥", f09fa4a5), +_c(U+1F926, "🤦", f09fa4a6), +_c(U+1F927, "🤧", f09fa4a7), +_c(U+1F928, "🤨", f09fa4a8), +_c(U+1F929, "🤩", f09fa4a9), +_c(U+1F92A, "🤪", f09fa4aa), +_c(U+1F92B, "🤫", f09fa4ab), +_c(U+1F92C, "🤬", f09fa4ac), +_c(U+1F92D, "🤭", f09fa4ad), +_c(U+1F92E, "🤮", f09fa4ae), +_c(U+1F92F, "🤯", f09fa4af), +_c(U+1F930, "🤰", f09fa4b0), +_c(U+1F931, "🤱", f09fa4b1), +_c(U+1F932, "🤲", f09fa4b2), +_c(U+1F933, "🤳", f09fa4b3), +_c(U+1F934, "🤴", f09fa4b4), +_c(U+1F935, "🤵", f09fa4b5), +_c(U+1F936, "🤶", f09fa4b6), +_c(U+1F937, "🤷", f09fa4b7), +_c(U+1F938, "🤸", f09fa4b8), +_c(U+1F939, "🤹", f09fa4b9), +_c(U+1F93A, "🤺", f09fa4ba), +_c(U+1F93B, "🤻", f09fa4bb), +_c(U+1F93C, "🤼", f09fa4bc), +_c(U+1F93D, "🤽", f09fa4bd), +_c(U+1F93E, "🤾", f09fa4be), +_c(U+1F93F, "🤿", f09fa4bf), +_c(U+1F940, "🥀", f09fa580), +_c(U+1F941, "🥁", f09fa581), +_c(U+1F942, "🥂", f09fa582), +_c(U+1F943, "🥃", f09fa583), +_c(U+1F944, "🥄", f09fa584), +_c(U+1F945, "🥅", f09fa585), +_c(U+1F946, "🥆", f09fa586), +_c(U+1F947, "🥇", f09fa587), +_c(U+1F948, "🥈", f09fa588), +_c(U+1F949, "🥉", f09fa589), +_c(U+1F94A, "🥊", f09fa58a), +_c(U+1F94B, "🥋", f09fa58b), +_c(U+1F94C, "🥌", f09fa58c), +_c(U+1F94D, "🥍", f09fa58d), +_c(U+1F94E, "🥎", f09fa58e), +_c(U+1F94F, "🥏", f09fa58f), +_c(U+1F950, "🥐", f09fa590), +_c(U+1F951, "🥑", f09fa591), +_c(U+1F952, "🥒", f09fa592), +_c(U+1F953, "🥓", f09fa593), +_c(U+1F954, "🥔", f09fa594), +_c(U+1F955, "🥕", f09fa595), +_c(U+1F956, "🥖", f09fa596), +_c(U+1F957, "🥗", f09fa597), +_c(U+1F958, "🥘", f09fa598), +_c(U+1F959, "🥙", f09fa599), +_c(U+1F95A, "🥚", f09fa59a), +_c(U+1F95B, "🥛", f09fa59b), +_c(U+1F95C, "🥜", f09fa59c), +_c(U+1F95D, "🥝", f09fa59d), +_c(U+1F95E, "🥞", f09fa59e), +_c(U+1F95F, "🥟", f09fa59f), +_c(U+1F960, "🥠", f09fa5a0), +_c(U+1F961, "🥡", f09fa5a1), +_c(U+1F962, "🥢", f09fa5a2), +_c(U+1F963, "🥣", f09fa5a3), +_c(U+1F964, "🥤", f09fa5a4), +_c(U+1F965, "🥥", f09fa5a5), +_c(U+1F966, "🥦", f09fa5a6), +_c(U+1F967, "🥧", f09fa5a7), +_c(U+1F968, "🥨", f09fa5a8), +_c(U+1F969, "🥩", f09fa5a9), +_c(U+1F96A, "🥪", f09fa5aa), +_c(U+1F96B, "🥫", f09fa5ab), +_c(U+1F96C, "🥬", f09fa5ac), +_c(U+1F96D, "🥭", f09fa5ad), +_c(U+1F96E, "🥮", f09fa5ae), +_c(U+1F96F, "🥯", f09fa5af), +_c(U+1F970, "🥰", f09fa5b0), +_c(U+1F971, "🥱", f09fa5b1), +_c(U+1F972, "🥲", f09fa5b2), +_c(U+1F973, "🥳", f09fa5b3), +_c(U+1F974, "🥴", f09fa5b4), +_c(U+1F975, "🥵", f09fa5b5), +_c(U+1F976, "🥶", f09fa5b6), +_c(U+1F977, "🥷", f09fa5b7), +_c(U+1F978, "🥸", f09fa5b8), +_c(U+1F979, "🥹", f09fa5b9), +_c(U+1F97A, "🥺", f09fa5ba), +_c(U+1F97B, "🥻", f09fa5bb), +_c(U+1F97C, "🥼", f09fa5bc), +_c(U+1F97D, "🥽", f09fa5bd), +_c(U+1F97E, "🥾", f09fa5be), +_c(U+1F97F, "🥿", f09fa5bf), +_c(U+1F980, "🦀", f09fa680), +_c(U+1F981, "🦁", f09fa681), +_c(U+1F982, "🦂", f09fa682), +_c(U+1F983, "🦃", f09fa683), +_c(U+1F984, "🦄", f09fa684), +_c(U+1F985, "🦅", f09fa685), +_c(U+1F986, "🦆", f09fa686), +_c(U+1F987, "🦇", f09fa687), +_c(U+1F988, "🦈", f09fa688), +_c(U+1F989, "🦉", f09fa689), +_c(U+1F98A, "🦊", f09fa68a), +_c(U+1F98B, "🦋", f09fa68b), +_c(U+1F98C, "🦌", f09fa68c), +_c(U+1F98D, "🦍", f09fa68d), +_c(U+1F98E, "🦎", f09fa68e), +_c(U+1F98F, "🦏", f09fa68f), +_c(U+1F990, "🦐", f09fa690), +_c(U+1F991, "🦑", f09fa691), +_c(U+1F992, "🦒", f09fa692), +_c(U+1F993, "🦓", f09fa693), +_c(U+1F994, "🦔", f09fa694), +_c(U+1F995, "🦕", f09fa695), +_c(U+1F996, "🦖", f09fa696), +_c(U+1F997, "🦗", f09fa697), +_c(U+1F998, "🦘", f09fa698), +_c(U+1F999, "🦙", f09fa699), +_c(U+1F99A, "🦚", f09fa69a), +_c(U+1F99B, "🦛", f09fa69b), +_c(U+1F99C, "🦜", f09fa69c), +_c(U+1F99D, "🦝", f09fa69d), +_c(U+1F99E, "🦞", f09fa69e), +_c(U+1F99F, "🦟", f09fa69f), +_c(U+1F9A0, "🦠", f09fa6a0), +_c(U+1F9A1, "🦡", f09fa6a1), +_c(U+1F9A2, "🦢", f09fa6a2), +_c(U+1F9A3, "🦣", f09fa6a3), +_c(U+1F9A4, "🦤", f09fa6a4), +_c(U+1F9A5, "🦥", f09fa6a5), +_c(U+1F9A6, "🦦", f09fa6a6), +_c(U+1F9A7, "🦧", f09fa6a7), +_c(U+1F9A8, "🦨", f09fa6a8), +_c(U+1F9A9, "🦩", f09fa6a9), +_c(U+1F9AA, "🦪", f09fa6aa), +_c(U+1F9AB, "🦫", f09fa6ab), +_c(U+1F9AC, "🦬", f09fa6ac), +_c(U+1F9AD, "🦭", f09fa6ad), +_c(U+1F9AE, "🦮", f09fa6ae), +_c(U+1F9AF, "🦯", f09fa6af), +_c(U+1F9B0, "🦰", f09fa6b0), +_c(U+1F9B1, "🦱", f09fa6b1), +_c(U+1F9B2, "🦲", f09fa6b2), +_c(U+1F9B3, "🦳", f09fa6b3), +_c(U+1F9B4, "🦴", f09fa6b4), +_c(U+1F9B5, "🦵", f09fa6b5), +_c(U+1F9B6, "🦶", f09fa6b6), +_c(U+1F9B7, "🦷", f09fa6b7), +_c(U+1F9B8, "🦸", f09fa6b8), +_c(U+1F9B9, "🦹", f09fa6b9), +_c(U+1F9BA, "🦺", f09fa6ba), +_c(U+1F9BB, "🦻", f09fa6bb), +_c(U+1F9BC, "🦼", f09fa6bc), +_c(U+1F9BD, "🦽", f09fa6bd), +_c(U+1F9BE, "🦾", f09fa6be), +_c(U+1F9BF, "🦿", f09fa6bf), +_c(U+1F9C0, "🧀", f09fa780), +_c(U+1F9C1, "🧁", f09fa781), +_c(U+1F9C2, "🧂", f09fa782), +_c(U+1F9C3, "🧃", f09fa783), +_c(U+1F9C4, "🧄", f09fa784), +_c(U+1F9C5, "🧅", f09fa785), +_c(U+1F9C6, "🧆", f09fa786), +_c(U+1F9C7, "🧇", f09fa787), +_c(U+1F9C8, "🧈", f09fa788), +_c(U+1F9C9, "🧉", f09fa789), +_c(U+1F9CA, "🧊", f09fa78a), +_c(U+1F9CB, "🧋", f09fa78b), +_c(U+1F9CC, "🧌", f09fa78c), +_c(U+1F9CD, "🧍", f09fa78d), +_c(U+1F9CE, "🧎", f09fa78e), +_c(U+1F9CF, "🧏", f09fa78f), +_c(U+1F9D0, "🧐", f09fa790), +_c(U+1F9D1, "🧑", f09fa791), +_c(U+1F9D2, "🧒", f09fa792), +_c(U+1F9D3, "🧓", f09fa793), +_c(U+1F9D4, "🧔", f09fa794), +_c(U+1F9D5, "🧕", f09fa795), +_c(U+1F9D6, "🧖", f09fa796), +_c(U+1F9D7, "🧗", f09fa797), +_c(U+1F9D8, "🧘", f09fa798), +_c(U+1F9D9, "🧙", f09fa799), +_c(U+1F9DA, "🧚", f09fa79a), +_c(U+1F9DB, "🧛", f09fa79b), +_c(U+1F9DC, "🧜", f09fa79c), +_c(U+1F9DD, "🧝", f09fa79d), +_c(U+1F9DE, "🧞", f09fa79e), +_c(U+1F9DF, "🧟", f09fa79f), +_c(U+1F9E0, "🧠", f09fa7a0), +_c(U+1F9E1, "🧡", f09fa7a1), +_c(U+1F9E2, "🧢", f09fa7a2), +_c(U+1F9E3, "🧣", f09fa7a3), +_c(U+1F9E4, "🧤", f09fa7a4), +_c(U+1F9E5, "🧥", f09fa7a5), +_c(U+1F9E6, "🧦", f09fa7a6), +_c(U+1F9E7, "🧧", f09fa7a7), +_c(U+1F9E8, "🧨", f09fa7a8), +_c(U+1F9E9, "🧩", f09fa7a9), +_c(U+1F9EA, "🧪", f09fa7aa), +_c(U+1F9EB, "🧫", f09fa7ab), +_c(U+1F9EC, "🧬", f09fa7ac), +_c(U+1F9ED, "🧭", f09fa7ad), +_c(U+1F9EE, "🧮", f09fa7ae), +_c(U+1F9EF, "🧯", f09fa7af), +_c(U+1F9F0, "🧰", f09fa7b0), +_c(U+1F9F1, "🧱", f09fa7b1), +_c(U+1F9F2, "🧲", f09fa7b2), +_c(U+1F9F3, "🧳", f09fa7b3), +_c(U+1F9F4, "🧴", f09fa7b4), +_c(U+1F9F5, "🧵", f09fa7b5), +_c(U+1F9F6, "🧶", f09fa7b6), +_c(U+1F9F7, "🧷", f09fa7b7), +_c(U+1F9F8, "🧸", f09fa7b8), +_c(U+1F9F9, "🧹", f09fa7b9), +_c(U+1F9FA, "🧺", f09fa7ba), +_c(U+1F9FB, "🧻", f09fa7bb), +_c(U+1F9FC, "🧼", f09fa7bc), +_c(U+1F9FD, "🧽", f09fa7bd), +_c(U+1F9FE, "🧾", f09fa7be), +_c(U+1F9FF, "🧿", f09fa7bf), +_c(U+100000, "", f4808080), +_c(U+100001, "", f4808081), +_c(U+100002, "", f4808082), +_c(U+100003, "", f4808083), +_c(U+100004, "", f4808084), +_c(U+100005, "", f4808085), +_c(U+100006, "", f4808086), +_c(U+100007, "", f4808087), +_c(U+100008, "", f4808088), +_c(U+100009, "", f4808089), +_c(U+10000A, "", f480808a), +_c(U+10000B, "", f480808b), +_c(U+10000C, "", f480808c), +_c(U+10000D, "", f480808d), +_c(U+10000E, "", f480808e), +_c(U+10000F, "", f480808f), +_c(U+100010, "", f4808090), +_c(U+100011, "", f4808091), +_c(U+100012, "", f4808092), +_c(U+100013, "", f4808093), +_c(U+100014, "", f4808094), +_c(U+100015, "", f4808095), +_c(U+100016, "", f4808096), +_c(U+100017, "", f4808097), +_c(U+100018, "", f4808098), +_c(U+100019, "", f4808099), +_c(U+10001A, "", f480809a), +_c(U+10001B, "", f480809b), +_c(U+10001C, "", f480809c), +_c(U+10001D, "", f480809d), +_c(U+10001E, "", f480809e), +_c(U+10001F, "", f480809f), +_c(U+100020, "", f48080a0), +_c(U+100021, "", f48080a1), +_c(U+100022, "", f48080a2), +_c(U+100023, "", f48080a3), +_c(U+100024, "", f48080a4), +_c(U+100025, "", f48080a5), +_c(U+100026, "", f48080a6), +_c(U+100027, "", f48080a7), +_c(U+100028, "", f48080a8), +_c(U+100029, "", f48080a9), +_c(U+10002A, "", f48080aa), +_c(U+10002B, "", f48080ab), +_c(U+10002C, "", f48080ac), +_c(U+10002D, "", f48080ad), +_c(U+10002E, "", f48080ae), +_c(U+10002F, "", f48080af), +_c(U+100030, "", f48080b0), +_c(U+100031, "", f48080b1), +_c(U+100032, "", f48080b2), +_c(U+100033, "", f48080b3), +_c(U+100034, "", f48080b4), +_c(U+100035, "", f48080b5), +_c(U+100036, "", f48080b6), +_c(U+100037, "", f48080b7), +_c(U+100038, "", f48080b8), +_c(U+100039, "", f48080b9), +_c(U+10003A, "", f48080ba), +_c(U+10003B, "", f48080bb), +_c(U+10003C, "", f48080bc), +_c(U+10003D, "", f48080bd), +_c(U+10003E, "", f48080be), +_c(U+10003F, "", f48080bf), +_c(U+100040, "", f4808180), +_c(U+100041, "", f4808181), +_c(U+100042, "", f4808182), +_c(U+100043, "", f4808183), +_c(U+100044, "", f4808184), +_c(U+100045, "", f4808185), +_c(U+100046, "", f4808186), +_c(U+100047, "", f4808187), +_c(U+100048, "", f4808188), +_c(U+100049, "", f4808189), +_c(U+10004A, "", f480818a), +_c(U+10004B, "", f480818b), +_c(U+10004C, "", f480818c), +_c(U+10004D, "", f480818d), +_c(U+10004E, "", f480818e), +_c(U+10004F, "", f480818f), +_c(U+100050, "", f4808190), +_c(U+100051, "", f4808191), +_c(U+100052, "", f4808192), +_c(U+100053, "", f4808193), +_c(U+100054, "", f4808194), +_c(U+100055, "", f4808195), +_c(U+100056, "", f4808196), +_c(U+100057, "", f4808197), +_c(U+100058, "", f4808198), +_c(U+100059, "", f4808199), +_c(U+10005A, "", f480819a), +_c(U+10005B, "", f480819b), +_c(U+10005C, "", f480819c), +_c(U+10005D, "", f480819d), +_c(U+10005E, "", f480819e), +_c(U+10005F, "", f480819f), +_c(U+100060, "", f48081a0), +_c(U+100061, "", f48081a1), +_c(U+100062, "", f48081a2), +_c(U+100063, "", f48081a3), +_c(U+100064, "", f48081a4), +_c(U+100065, "", f48081a5), +_c(U+100066, "", f48081a6), +_c(U+100067, "", f48081a7), +_c(U+100068, "", f48081a8), +_c(U+100069, "", f48081a9), +_c(U+10006A, "", f48081aa), +_c(U+10006B, "", f48081ab), +_c(U+10006C, "", f48081ac), +_c(U+10006D, "", f48081ad), +_c(U+10006E, "", f48081ae), +_c(U+10006F, "", f48081af), +_c(U+100070, "", f48081b0), +_c(U+100071, "", f48081b1), +_c(U+100072, "", f48081b2), +_c(U+100073, "", f48081b3), +_c(U+100074, "", f48081b4), +_c(U+100075, "", f48081b5), +_c(U+100076, "", f48081b6), +_c(U+100077, "", f48081b7), +_c(U+100078, "", f48081b8), +_c(U+100079, "", f48081b9), +_c(U+10007A, "", f48081ba), +_c(U+10007B, "", f48081bb), +_c(U+10007C, "", f48081bc), +_c(U+10007D, "", f48081bd), +_c(U+10007E, "", f48081be), +_c(U+10007F, "", f48081bf), +_c(U+100080, "", f4808280), +_c(U+100081, "", f4808281), +_c(U+100082, "", f4808282), +_c(U+100083, "", f4808283), +_c(U+100084, "", f4808284), +_c(U+100085, "", f4808285), +_c(U+100086, "", f4808286), +_c(U+100087, "", f4808287), +_c(U+100088, "", f4808288), +_c(U+100089, "", f4808289), +_c(U+10008A, "", f480828a), +_c(U+10008B, "", f480828b), +_c(U+10008C, "", f480828c), +_c(U+10008D, "", f480828d), +_c(U+10008E, "", f480828e), +_c(U+10008F, "", f480828f), +_c(U+100090, "", f4808290), +_c(U+100091, "", f4808291), +_c(U+100092, "", f4808292), +_c(U+100093, "", f4808293), +_c(U+100094, "", f4808294), +_c(U+100095, "", f4808295), +_c(U+100096, "", f4808296), +_c(U+100097, "", f4808297), +_c(U+100098, "", f4808298), +_c(U+100099, "", f4808299), +_c(U+10009A, "", f480829a), +_c(U+10009B, "", f480829b), +_c(U+10009C, "", f480829c), +_c(U+10009D, "", f480829d), +_c(U+10009E, "", f480829e), +_c(U+10009F, "", f480829f), +_c(U+1000A0, "", f48082a0), +_c(U+1000A1, "", f48082a1), +_c(U+1000A2, "", f48082a2), +_c(U+1000A3, "", f48082a3), +_c(U+1000A4, "", f48082a4), +_c(U+1000A5, "", f48082a5), +_c(U+1000A6, "", f48082a6), +_c(U+1000A7, "", f48082a7), +_c(U+1000A8, "", f48082a8), +_c(U+1000A9, "", f48082a9), +_c(U+1000AA, "", f48082aa), +_c(U+1000AB, "", f48082ab), +_c(U+1000AC, "", f48082ac), +_c(U+1000AD, "", f48082ad), +_c(U+1000AE, "", f48082ae), +_c(U+1000AF, "", f48082af), +_c(U+1000B0, "", f48082b0), +_c(U+1000B1, "", f48082b1), +_c(U+1000B2, "", f48082b2), +_c(U+1000B3, "", f48082b3), +_c(U+1000B4, "", f48082b4), +_c(U+1000B5, "", f48082b5), +_c(U+1000B6, "", f48082b6), +_c(U+1000B7, "", f48082b7), +_c(U+1000B8, "", f48082b8), +_c(U+1000B9, "", f48082b9), +_c(U+1000BA, "", f48082ba), +_c(U+1000BB, "", f48082bb), +_c(U+1000BC, "", f48082bc), +_c(U+1000BD, "", f48082bd), +_c(U+1000BE, "", f48082be), +_c(U+1000BF, "", f48082bf), +_c(U+1000C0, "", f4808380), +_c(U+1000C1, "", f4808381), +_c(U+1000C2, "", f4808382), +_c(U+1000C3, "", f4808383), +_c(U+1000C4, "", f4808384), +_c(U+1000C5, "", f4808385), +_c(U+1000C6, "", f4808386), +_c(U+1000C7, "", f4808387), +_c(U+1000C8, "", f4808388), +_c(U+1000C9, "", f4808389), +_c(U+1000CA, "", f480838a), +_c(U+1000CB, "", f480838b), +_c(U+1000CC, "", f480838c), +_c(U+1000CD, "", f480838d), +_c(U+1000CE, "", f480838e), +_c(U+1000CF, "", f480838f), +_c(U+1000D0, "", f4808390), +_c(U+1000D1, "", f4808391), +_c(U+1000D2, "", f4808392), +_c(U+1000D3, "", f4808393), +_c(U+1000D4, "", f4808394), +_c(U+1000D5, "", f4808395), +_c(U+1000D6, "", f4808396), +_c(U+1000D7, "", f4808397), +_c(U+1000D8, "", f4808398), +_c(U+1000D9, "", f4808399), +_c(U+1000DA, "", f480839a), +_c(U+1000DB, "", f480839b), +_c(U+1000DC, "", f480839c), +_c(U+1000DD, "", f480839d), +_c(U+1000DE, "", f480839e), +_c(U+1000DF, "", f480839f), +_c(U+1000E0, "", f48083a0), +_c(U+1000E1, "", f48083a1), +_c(U+1000E2, "", f48083a2), +_c(U+1000E3, "", f48083a3), +_c(U+1000E4, "", f48083a4), +_c(U+1000E5, "", f48083a5), +_c(U+1000E6, "", f48083a6), +_c(U+1000E7, "", f48083a7), +_c(U+1000E8, "", f48083a8), +_c(U+1000E9, "", f48083a9), +_c(U+1000EA, "", f48083aa), +_c(U+1000EB, "", f48083ab), +_c(U+1000EC, "", f48083ac), +_c(U+1000ED, "", f48083ad), +_c(U+1000EE, "", f48083ae), +_c(U+1000EF, "", f48083af), +_c(U+1000F0, "", f48083b0), +_c(U+1000F1, "", f48083b1), +_c(U+1000F2, "", f48083b2), +_c(U+1000F3, "", f48083b3), +_c(U+1000F4, "", f48083b4), +_c(U+1000F5, "", f48083b5), +_c(U+1000F6, "", f48083b6), +_c(U+1000F7, "", f48083b7), +_c(U+1000F8, "", f48083b8), +_c(U+1000F9, "", f48083b9), +_c(U+1000FA, "", f48083ba), +_c(U+1000FB, "", f48083bb), +_c(U+1000FC, "", f48083bc), +_c(U+1000FD, "", f48083bd), +_c(U+1000FE, "", f48083be), +_c(U+1000FF, "", f48083bf), +_c(U+100100, "", f4808480), +_c(U+100101, "", f4808481), +_c(U+100102, "", f4808482), +_c(U+100103, "", f4808483), +_c(U+100104, "", f4808484), +_c(U+100105, "", f4808485), +_c(U+100106, "", f4808486), +_c(U+100107, "", f4808487), +_c(U+100108, "", f4808488), +_c(U+100109, "", f4808489), +_c(U+10010A, "", f480848a), +_c(U+10010B, "", f480848b), +_c(U+10010C, "", f480848c), +_c(U+10010D, "", f480848d), +_c(U+10010E, "", f480848e), +_c(U+10010F, "", f480848f), +_c(U+100110, "", f4808490), +_c(U+100111, "", f4808491), +_c(U+100112, "", f4808492), +_c(U+100113, "", f4808493), +_c(U+100114, "", f4808494), +_c(U+100115, "", f4808495), +_c(U+100116, "", f4808496), +_c(U+100117, "", f4808497), +_c(U+100118, "", f4808498), +_c(U+100119, "", f4808499), +_c(U+10011A, "", f480849a), +_c(U+10011B, "", f480849b), +_c(U+10011C, "", f480849c), +_c(U+10011D, "", f480849d), +_c(U+10011E, "", f480849e), +_c(U+10011F, "", f480849f), +_c(U+100120, "", f48084a0), +_c(U+100121, "", f48084a1), +_c(U+100122, "", f48084a2), +_c(U+100123, "", f48084a3), +_c(U+100124, "", f48084a4), +_c(U+100125, "", f48084a5), +_c(U+100126, "", f48084a6), +_c(U+100127, "", f48084a7), +_c(U+100128, "", f48084a8), +_c(U+100129, "", f48084a9), +_c(U+10012A, "", f48084aa), +_c(U+10012B, "", f48084ab), +_c(U+10012C, "", f48084ac), +_c(U+10012D, "", f48084ad), +_c(U+10012E, "", f48084ae), +_c(U+10012F, "", f48084af), +_c(U+100130, "", f48084b0), +_c(U+100131, "", f48084b1), +_c(U+100132, "", f48084b2), +_c(U+100133, "", f48084b3), +_c(U+100134, "", f48084b4), +_c(U+100135, "", f48084b5), +_c(U+100136, "", f48084b6), +_c(U+100137, "", f48084b7), +_c(U+100138, "", f48084b8), +_c(U+100139, "", f48084b9), +_c(U+10013A, "", f48084ba), +_c(U+10013B, "", f48084bb), +_c(U+10013C, "", f48084bc), +_c(U+10013D, "", f48084bd), +_c(U+10013E, "", f48084be), +_c(U+10013F, "", f48084bf), +_c(U+100140, "", f4808580), +_c(U+100141, "", f4808581), +_c(U+100142, "", f4808582), +_c(U+100143, "", f4808583), +_c(U+100144, "", f4808584), +_c(U+100145, "", f4808585), +_c(U+100146, "", f4808586), +_c(U+100147, "", f4808587), +_c(U+100148, "", f4808588), +_c(U+100149, "", f4808589), +_c(U+10014A, "", f480858a), +_c(U+10014B, "", f480858b), +_c(U+10014C, "", f480858c), +_c(U+10014D, "", f480858d), +_c(U+10014E, "", f480858e), +_c(U+10014F, "", f480858f), +_c(U+100150, "", f4808590), +_c(U+100151, "", f4808591), +_c(U+100152, "", f4808592), +_c(U+100153, "", f4808593), +_c(U+100154, "", f4808594), +_c(U+100155, "", f4808595), +_c(U+100156, "", f4808596), +_c(U+100157, "", f4808597), +_c(U+100158, "", f4808598), +_c(U+100159, "", f4808599), +_c(U+10015A, "", f480859a), +_c(U+10015B, "", f480859b), +_c(U+10015C, "", f480859c), +_c(U+10015D, "", f480859d), +_c(U+10015E, "", f480859e), +_c(U+10015F, "", f480859f), +_c(U+100160, "", f48085a0), +_c(U+100161, "", f48085a1), +_c(U+100162, "", f48085a2), +_c(U+100163, "", f48085a3), +_c(U+100164, "", f48085a4), +_c(U+100165, "", f48085a5), +_c(U+100166, "", f48085a6), +_c(U+100167, "", f48085a7), +_c(U+100168, "", f48085a8), +_c(U+100169, "", f48085a9), +_c(U+10016A, "", f48085aa), +_c(U+10016B, "", f48085ab), +_c(U+10016C, "", f48085ac), +_c(U+10016D, "", f48085ad), +_c(U+10016E, "", f48085ae), +_c(U+10016F, "", f48085af), +_c(U+100170, "", f48085b0), +_c(U+100171, "", f48085b1), +_c(U+100172, "", f48085b2), +_c(U+100173, "", f48085b3), +_c(U+100174, "", f48085b4), +_c(U+100175, "", f48085b5), +_c(U+100176, "", f48085b6), +_c(U+100177, "", f48085b7), +_c(U+100178, "", f48085b8), +_c(U+100179, "", f48085b9), +_c(U+10017A, "", f48085ba), +_c(U+10017B, "", f48085bb), +_c(U+10017C, "", f48085bc), +_c(U+10017D, "", f48085bd), +_c(U+10017E, "", f48085be), +_c(U+10017F, "", f48085bf), +_c(U+100180, "", f4808680), +_c(U+100181, "", f4808681), +_c(U+100182, "", f4808682), +_c(U+100183, "", f4808683), +_c(U+100184, "", f4808684), +_c(U+100185, "", f4808685), +_c(U+100186, "", f4808686), +_c(U+100187, "", f4808687), +_c(U+100188, "", f4808688), +_c(U+100189, "", f4808689), +_c(U+10018A, "", f480868a), +_c(U+10018B, "", f480868b), +_c(U+10018C, "", f480868c), +_c(U+10018D, "", f480868d), +_c(U+10018E, "", f480868e), +_c(U+10018F, "", f480868f), +_c(U+100190, "", f4808690), +_c(U+100191, "", f4808691), +_c(U+100192, "", f4808692), +_c(U+100193, "", f4808693), +_c(U+100194, "", f4808694), +_c(U+100195, "", f4808695), +_c(U+100196, "", f4808696), +_c(U+100197, "", f4808697), +_c(U+100198, "", f4808698), +_c(U+100199, "", f4808699), +_c(U+10019A, "", f480869a), +_c(U+10019B, "", f480869b), +_c(U+10019C, "", f480869c), +_c(U+10019D, "", f480869d), +_c(U+10019E, "", f480869e), +_c(U+10019F, "", f480869f), +_c(U+1001A0, "", f48086a0), +_c(U+1001A1, "", f48086a1), +_c(U+1001A2, "", f48086a2), +_c(U+1001A3, "", f48086a3), +_c(U+1001A4, "", f48086a4), +_c(U+1001A5, "", f48086a5), +_c(U+1001A6, "", f48086a6), +_c(U+1001A7, "", f48086a7), +_c(U+1001A8, "", f48086a8), +_c(U+1001A9, "", f48086a9), +_c(U+1001AA, "", f48086aa), +_c(U+1001AB, "", f48086ab), +_c(U+1001AC, "", f48086ac), +_c(U+1001AD, "", f48086ad), +_c(U+1001AE, "", f48086ae), +_c(U+1001AF, "", f48086af), +_c(U+1001B0, "", f48086b0), +_c(U+1001B1, "", f48086b1), +_c(U+1001B2, "", f48086b2), +_c(U+1001B3, "", f48086b3), +_c(U+1001B4, "", f48086b4), +_c(U+1001B5, "", f48086b5), +_c(U+1001B6, "", f48086b6), +_c(U+1001B7, "", f48086b7), +_c(U+1001B8, "", f48086b8), +_c(U+1001B9, "", f48086b9), +_c(U+1001BA, "", f48086ba), +_c(U+1001BB, "", f48086bb), +_c(U+1001BC, "", f48086bc), +_c(U+1001BD, "", f48086bd), +_c(U+1001BE, "", f48086be), +_c(U+1001BF, "", f48086bf), +_c(U+1001C0, "", f4808780), +_c(U+1001C1, "", f4808781), +_c(U+1001C2, "", f4808782), +_c(U+1001C3, "", f4808783), +_c(U+1001C4, "", f4808784), +_c(U+1001C5, "", f4808785), +_c(U+1001C6, "", f4808786), +_c(U+1001C7, "", f4808787), +_c(U+1001C8, "", f4808788), +_c(U+1001C9, "", f4808789), +_c(U+1001CA, "", f480878a), +_c(U+1001CB, "", f480878b), +_c(U+1001CC, "", f480878c), +_c(U+1001CD, "", f480878d), +_c(U+1001CE, "", f480878e), +_c(U+1001CF, "", f480878f), +_c(U+1001D0, "", f4808790), +_c(U+1001D1, "", f4808791), +_c(U+1001D2, "", f4808792), +_c(U+1001D3, "", f4808793), +_c(U+1001D4, "", f4808794), +_c(U+1001D5, "", f4808795), +_c(U+1001D6, "", f4808796), +_c(U+1001D7, "", f4808797), +_c(U+1001D8, "", f4808798), +_c(U+1001D9, "", f4808799), +_c(U+1001DA, "", f480879a), +_c(U+1001DB, "", f480879b), +_c(U+1001DC, "", f480879c), +_c(U+1001DD, "", f480879d), +_c(U+1001DE, "", f480879e), +_c(U+1001DF, "", f480879f), +_c(U+1001E0, "", f48087a0), +_c(U+1001E1, "", f48087a1), +_c(U+1001E2, "", f48087a2), +_c(U+1001E3, "", f48087a3), +_c(U+1001E4, "", f48087a4), +_c(U+1001E5, "", f48087a5), +_c(U+1001E6, "", f48087a6), +_c(U+1001E7, "", f48087a7), +_c(U+1001E8, "", f48087a8), +_c(U+1001E9, "", f48087a9), +_c(U+1001EA, "", f48087aa), +_c(U+1001EB, "", f48087ab), +_c(U+1001EC, "", f48087ac), +_c(U+1001ED, "", f48087ad), +_c(U+1001EE, "", f48087ae), +_c(U+1001EF, "", f48087af), +_c(U+1001F0, "", f48087b0), +_c(U+1001F1, "", f48087b1), +_c(U+1001F2, "", f48087b2), +_c(U+1001F3, "", f48087b3), +_c(U+1001F4, "", f48087b4), +_c(U+1001F5, "", f48087b5), +_c(U+1001F6, "", f48087b6), +_c(U+1001F7, "", f48087b7), +_c(U+1001F8, "", f48087b8), +_c(U+1001F9, "", f48087b9), +_c(U+1001FA, "", f48087ba), +_c(U+1001FB, "", f48087bb), +_c(U+1001FC, "", f48087bc), +_c(U+1001FD, "", f48087bd), +_c(U+1001FE, "", f48087be), +_c(U+1001FF, "", f48087bf), +_c(U+100200, "", f4808880), +_c(U+100201, "", f4808881), +_c(U+100202, "", f4808882), +_c(U+100203, "", f4808883), +_c(U+100204, "", f4808884), +_c(U+100205, "", f4808885), +_c(U+100206, "", f4808886), +_c(U+100207, "", f4808887), +_c(U+100208, "", f4808888), +_c(U+100209, "", f4808889), +_c(U+10020A, "", f480888a), +_c(U+10020B, "", f480888b), +_c(U+10020C, "", f480888c), +_c(U+10020D, "", f480888d), +_c(U+10020E, "", f480888e), +_c(U+10020F, "", f480888f), +_c(U+100210, "", f4808890), +_c(U+100211, "", f4808891), +_c(U+100212, "", f4808892), +_c(U+100213, "", f4808893), +_c(U+100214, "", f4808894), +_c(U+100215, "", f4808895), +_c(U+100216, "", f4808896), +_c(U+100217, "", f4808897), +_c(U+100218, "", f4808898), +_c(U+100219, "", f4808899), +_c(U+10021A, "", f480889a), +_c(U+10021B, "", f480889b), +_c(U+10021C, "", f480889c), +_c(U+10021D, "", f480889d), +_c(U+10021E, "", f480889e), +_c(U+10021F, "", f480889f), +_c(U+100220, "", f48088a0), +_c(U+100221, "", f48088a1), +_c(U+100222, "", f48088a2), +_c(U+100223, "", f48088a3), +_c(U+100224, "", f48088a4), +_c(U+100225, "", f48088a5), +_c(U+100226, "", f48088a6), +_c(U+100227, "", f48088a7), +_c(U+100228, "", f48088a8), +_c(U+100229, "", f48088a9), +_c(U+10022A, "", f48088aa), +_c(U+10022B, "", f48088ab), +_c(U+10022C, "", f48088ac), +_c(U+10022D, "", f48088ad), +_c(U+10022E, "", f48088ae), +_c(U+10022F, "", f48088af), +_c(U+100230, "", f48088b0), +_c(U+100231, "", f48088b1), +_c(U+100232, "", f48088b2), +_c(U+100233, "", f48088b3), +_c(U+100234, "", f48088b4), +_c(U+100235, "", f48088b5), +_c(U+100236, "", f48088b6), +_c(U+100237, "", f48088b7), +_c(U+100238, "", f48088b8), +_c(U+100239, "", f48088b9), +_c(U+10023A, "", f48088ba), +_c(U+10023B, "", f48088bb), +_c(U+10023C, "", f48088bc), +_c(U+10023D, "", f48088bd), +_c(U+10023E, "", f48088be), +_c(U+10023F, "", f48088bf), +_c(U+100240, "", f4808980), +_c(U+100241, "", f4808981), +_c(U+100242, "", f4808982), +_c(U+100243, "", f4808983), +_c(U+100244, "", f4808984), +_c(U+100245, "", f4808985), +_c(U+100246, "", f4808986), +_c(U+100247, "", f4808987), +_c(U+100248, "", f4808988), +_c(U+100249, "", f4808989), +_c(U+10024A, "", f480898a), +_c(U+10024B, "", f480898b), +_c(U+10024C, "", f480898c), +_c(U+10024D, "", f480898d), +_c(U+10024E, "", f480898e), +_c(U+10024F, "", f480898f), +_c(U+100250, "", f4808990), +_c(U+100251, "", f4808991), +_c(U+100252, "", f4808992), +_c(U+100253, "", f4808993), +_c(U+100254, "", f4808994), +_c(U+100255, "", f4808995), +_c(U+100256, "", f4808996), +_c(U+100257, "", f4808997), +_c(U+100258, "", f4808998), +_c(U+100259, "", f4808999), +_c(U+10025A, "", f480899a), +_c(U+10025B, "", f480899b), +_c(U+10025C, "", f480899c), +_c(U+10025D, "", f480899d), +_c(U+10025E, "", f480899e), +_c(U+10025F, "", f480899f), +_c(U+100260, "", f48089a0), +_c(U+100261, "", f48089a1), +_c(U+100262, "", f48089a2), +_c(U+100263, "", f48089a3), +_c(U+100264, "", f48089a4), +_c(U+100265, "", f48089a5), +_c(U+100266, "", f48089a6), +_c(U+100267, "", f48089a7), +_c(U+100268, "", f48089a8), +_c(U+100269, "", f48089a9), +_c(U+10026A, "", f48089aa), +_c(U+10026B, "", f48089ab), +_c(U+10026C, "", f48089ac), +_c(U+10026D, "", f48089ad), +_c(U+10026E, "", f48089ae), +_c(U+10026F, "", f48089af), +_c(U+100270, "", f48089b0), +_c(U+100271, "", f48089b1), +_c(U+100272, "", f48089b2), +_c(U+100273, "", f48089b3), +_c(U+100274, "", f48089b4), +_c(U+100275, "", f48089b5), +_c(U+100276, "", f48089b6), +_c(U+100277, "", f48089b7), +_c(U+100278, "", f48089b8), +_c(U+100279, "", f48089b9), +_c(U+10027A, "", f48089ba), +_c(U+10027B, "", f48089bb), +_c(U+10027C, "", f48089bc), +_c(U+10027D, "", f48089bd), +_c(U+10027E, "", f48089be), +_c(U+10027F, "", f48089bf), +_c(U+100280, "", f4808a80), +_c(U+100281, "", f4808a81), +_c(U+100282, "", f4808a82), +_c(U+100283, "", f4808a83), +_c(U+100284, "", f4808a84), +_c(U+100285, "", f4808a85), +_c(U+100286, "", f4808a86), +_c(U+100287, "", f4808a87), +_c(U+100288, "", f4808a88), +_c(U+100289, "", f4808a89), +_c(U+10028A, "", f4808a8a), +_c(U+10028B, "", f4808a8b), +_c(U+10028C, "", f4808a8c), +_c(U+10028D, "", f4808a8d), +_c(U+10028E, "", f4808a8e), +_c(U+10028F, "", f4808a8f), +_c(U+100290, "", f4808a90), +_c(U+100291, "", f4808a91), +_c(U+100292, "", f4808a92), +_c(U+100293, "", f4808a93), +_c(U+100294, "", f4808a94), +_c(U+100295, "", f4808a95), +_c(U+100296, "", f4808a96), +_c(U+100297, "", f4808a97), +_c(U+100298, "", f4808a98), +_c(U+100299, "", f4808a99), +_c(U+10029A, "", f4808a9a), +_c(U+10029B, "", f4808a9b), +_c(U+10029C, "", f4808a9c), +_c(U+10029D, "", f4808a9d), +_c(U+10029E, "", f4808a9e), +_c(U+10029F, "", f4808a9f), +_c(U+1002A0, "", f4808aa0), +_c(U+1002A1, "", f4808aa1), +_c(U+1002A2, "", f4808aa2), +_c(U+1002A3, "", f4808aa3), +_c(U+1002A4, "", f4808aa4), +_c(U+1002A5, "", f4808aa5), +_c(U+1002A6, "", f4808aa6), +_c(U+1002A7, "", f4808aa7), +_c(U+1002A8, "", f4808aa8), +_c(U+1002A9, "", f4808aa9), +_c(U+1002AA, "", f4808aaa), +_c(U+1002AB, "", f4808aab), +_c(U+1002AC, "", f4808aac), +_c(U+1002AD, "", f4808aad), +_c(U+1002AE, "", f4808aae), +_c(U+1002AF, "", f4808aaf), +_c(U+1002B0, "", f4808ab0), +_c(U+1002B1, "", f4808ab1), +_c(U+1002B2, "", f4808ab2), +_c(U+1002B3, "", f4808ab3), +_c(U+1002B4, "", f4808ab4), +_c(U+1002B5, "", f4808ab5), +_c(U+1002B6, "", f4808ab6), +_c(U+1002B7, "", f4808ab7), +_c(U+1002B8, "", f4808ab8), +_c(U+1002B9, "", f4808ab9), +_c(U+1002BA, "", f4808aba), +_c(U+1002BB, "", f4808abb), +_c(U+1002BC, "", f4808abc), +_c(U+1002BD, "", f4808abd), +_c(U+1002BE, "", f4808abe), +_c(U+1002BF, "", f4808abf), +_c(U+1002C0, "", f4808b80), +_c(U+1002C1, "", f4808b81), +_c(U+1002C2, "", f4808b82), +_c(U+1002C3, "", f4808b83), +_c(U+1002C4, "", f4808b84), +_c(U+1002C5, "", f4808b85), +_c(U+1002C6, "", f4808b86), +_c(U+1002C7, "", f4808b87), +_c(U+1002C8, "", f4808b88), +_c(U+1002C9, "", f4808b89), +_c(U+1002CA, "", f4808b8a), +_c(U+1002CB, "", f4808b8b), +_c(U+1002CC, "", f4808b8c), +_c(U+1002CD, "", f4808b8d), +_c(U+1002CE, "", f4808b8e), +_c(U+1002CF, "", f4808b8f), +_c(U+1002D0, "", f4808b90), +_c(U+1002D1, "", f4808b91), +_c(U+1002D2, "", f4808b92), +_c(U+1002D3, "", f4808b93), +_c(U+1002D4, "", f4808b94), +_c(U+1002D5, "", f4808b95), +_c(U+1002D6, "", f4808b96), +_c(U+1002D7, "", f4808b97), +_c(U+1002D8, "", f4808b98), +_c(U+1002D9, "", f4808b99), +_c(U+1002DA, "", f4808b9a), +_c(U+1002DB, "", f4808b9b), +_c(U+1002DC, "", f4808b9c), +_c(U+1002DD, "", f4808b9d), +_c(U+1002DE, "", f4808b9e), +_c(U+1002DF, "", f4808b9f), +_c(U+1002E0, "", f4808ba0), +_c(U+1002E1, "", f4808ba1), +_c(U+1002E2, "", f4808ba2), +_c(U+1002E3, "", f4808ba3), +_c(U+1002E4, "", f4808ba4), +_c(U+1002E5, "", f4808ba5), +_c(U+1002E6, "", f4808ba6), +_c(U+1002E7, "", f4808ba7), +_c(U+1002E8, "", f4808ba8), +_c(U+1002E9, "", f4808ba9), +_c(U+1002EA, "", f4808baa), +_c(U+1002EB, "", f4808bab), +_c(U+1002EC, "", f4808bac), +_c(U+1002ED, "", f4808bad), +_c(U+1002EE, "", f4808bae), +_c(U+1002EF, "", f4808baf), +_c(U+1002F0, "", f4808bb0), +_c(U+1002F1, "", f4808bb1), +_c(U+1002F2, "", f4808bb2), +_c(U+1002F3, "", f4808bb3), +_c(U+1002F4, "", f4808bb4), +_c(U+1002F5, "", f4808bb5), +_c(U+1002F6, "", f4808bb6), +_c(U+1002F7, "", f4808bb7), +_c(U+1002F8, "", f4808bb8), +_c(U+1002F9, "", f4808bb9), +_c(U+1002FA, "", f4808bba), +_c(U+1002FB, "", f4808bbb), +_c(U+1002FC, "", f4808bbc), +_c(U+1002FD, "", f4808bbd), +_c(U+1002FE, "", f4808bbe), +_c(U+1002FF, "", f4808bbf), +_c(U+100300, "", f4808c80), +_c(U+100301, "", f4808c81), +_c(U+100302, "", f4808c82), +_c(U+100303, "", f4808c83), +_c(U+100304, "", f4808c84), +_c(U+100305, "", f4808c85), +_c(U+100306, "", f4808c86), +_c(U+100307, "", f4808c87), +_c(U+100308, "", f4808c88), +_c(U+100309, "", f4808c89), +_c(U+10030A, "", f4808c8a), +_c(U+10030B, "", f4808c8b), +_c(U+10030C, "", f4808c8c), +_c(U+10030D, "", f4808c8d), +_c(U+10030E, "", f4808c8e), +_c(U+10030F, "", f4808c8f), +_c(U+100310, "", f4808c90), +_c(U+100311, "", f4808c91), +_c(U+100312, "", f4808c92), +_c(U+100313, "", f4808c93), +_c(U+100314, "", f4808c94), +_c(U+100315, "", f4808c95), +_c(U+100316, "", f4808c96), +_c(U+100317, "", f4808c97), +_c(U+100318, "", f4808c98), +_c(U+100319, "", f4808c99), +_c(U+10031A, "", f4808c9a), +_c(U+10031B, "", f4808c9b), +_c(U+10031C, "", f4808c9c), +_c(U+10031D, "", f4808c9d), +_c(U+10031E, "", f4808c9e), +_c(U+10031F, "", f4808c9f), +_c(U+100320, "", f4808ca0), +_c(U+100321, "", f4808ca1), +_c(U+100322, "", f4808ca2), +_c(U+100323, "", f4808ca3), +_c(U+100324, "", f4808ca4), +_c(U+100325, "", f4808ca5), +_c(U+100326, "", f4808ca6), +_c(U+100327, "", f4808ca7), +_c(U+100328, "", f4808ca8), +_c(U+100329, "", f4808ca9), +_c(U+10032A, "", f4808caa), +_c(U+10032B, "", f4808cab), +_c(U+10032C, "", f4808cac), +_c(U+10032D, "", f4808cad), +_c(U+10032E, "", f4808cae), +_c(U+10032F, "", f4808caf), +_c(U+100330, "", f4808cb0), +_c(U+100331, "", f4808cb1), +_c(U+100332, "", f4808cb2), +_c(U+100333, "", f4808cb3), +_c(U+100334, "", f4808cb4), +_c(U+100335, "", f4808cb5), +_c(U+100336, "", f4808cb6), +_c(U+100337, "", f4808cb7), +_c(U+100338, "", f4808cb8), +_c(U+100339, "", f4808cb9), +_c(U+10033A, "", f4808cba), +_c(U+10033B, "", f4808cbb), +_c(U+10033C, "", f4808cbc), +_c(U+10033D, "", f4808cbd), +_c(U+10033E, "", f4808cbe), +_c(U+10033F, "", f4808cbf), +_c(U+100340, "", f4808d80), +_c(U+100341, "", f4808d81), +_c(U+100342, "", f4808d82), +_c(U+100343, "", f4808d83), +_c(U+100344, "", f4808d84), +_c(U+100345, "", f4808d85), +_c(U+100346, "", f4808d86), +_c(U+100347, "", f4808d87), +_c(U+100348, "", f4808d88), +_c(U+100349, "", f4808d89), +_c(U+10034A, "", f4808d8a), +_c(U+10034B, "", f4808d8b), +_c(U+10034C, "", f4808d8c), +_c(U+10034D, "", f4808d8d), +_c(U+10034E, "", f4808d8e), +_c(U+10034F, "", f4808d8f), +_c(U+100350, "", f4808d90), +_c(U+100351, "", f4808d91), +_c(U+100352, "", f4808d92), +_c(U+100353, "", f4808d93), +_c(U+100354, "", f4808d94), +_c(U+100355, "", f4808d95), +_c(U+100356, "", f4808d96), +_c(U+100357, "", f4808d97), +_c(U+100358, "", f4808d98), +_c(U+100359, "", f4808d99), +_c(U+10035A, "", f4808d9a), +_c(U+10035B, "", f4808d9b), +_c(U+10035C, "", f4808d9c), +_c(U+10035D, "", f4808d9d), +_c(U+10035E, "", f4808d9e), +_c(U+10035F, "", f4808d9f), +_c(U+100360, "", f4808da0), +_c(U+100361, "", f4808da1), +_c(U+100362, "", f4808da2), +_c(U+100363, "", f4808da3), +_c(U+100364, "", f4808da4), +_c(U+100365, "", f4808da5), +_c(U+100366, "", f4808da6), +_c(U+100367, "", f4808da7), +_c(U+100368, "", f4808da8), +_c(U+100369, "", f4808da9), +_c(U+10036A, "", f4808daa), +_c(U+10036B, "", f4808dab), +_c(U+10036C, "", f4808dac), +_c(U+10036D, "", f4808dad), +_c(U+10036E, "", f4808dae), +_c(U+10036F, "", f4808daf), +_c(U+100370, "", f4808db0), +_c(U+100371, "", f4808db1), +_c(U+100372, "", f4808db2), +_c(U+100373, "", f4808db3), +_c(U+100374, "", f4808db4), +_c(U+100375, "", f4808db5), +_c(U+100376, "", f4808db6), +_c(U+100377, "", f4808db7), +_c(U+100378, "", f4808db8), +_c(U+100379, "", f4808db9), +_c(U+10037A, "", f4808dba), +_c(U+10037B, "", f4808dbb), +_c(U+10037C, "", f4808dbc), +_c(U+10037D, "", f4808dbd), +_c(U+10037E, "", f4808dbe), +_c(U+10037F, "", f4808dbf), +_c(U+100380, "", f4808e80), +_c(U+100381, "", f4808e81), +_c(U+100382, "", f4808e82), +_c(U+100383, "", f4808e83), +_c(U+100384, "", f4808e84), +_c(U+100385, "", f4808e85), +_c(U+100386, "", f4808e86), +_c(U+100387, "", f4808e87), +_c(U+100388, "", f4808e88), +_c(U+100389, "", f4808e89), +_c(U+10038A, "", f4808e8a), +_c(U+10038B, "", f4808e8b), +_c(U+10038C, "", f4808e8c), +_c(U+10038D, "", f4808e8d), +_c(U+10038E, "", f4808e8e), +_c(U+10038F, "", f4808e8f), +_c(U+100390, "", f4808e90), +_c(U+100391, "", f4808e91), +_c(U+100392, "", f4808e92), +_c(U+100393, "", f4808e93), +_c(U+100394, "", f4808e94), +_c(U+100395, "", f4808e95), +_c(U+100396, "", f4808e96), +_c(U+100397, "", f4808e97), +_c(U+100398, "", f4808e98), +_c(U+100399, "", f4808e99), +_c(U+10039A, "", f4808e9a), +_c(U+10039B, "", f4808e9b), +_c(U+10039C, "", f4808e9c), +_c(U+10039D, "", f4808e9d), +_c(U+10039E, "", f4808e9e), +_c(U+10039F, "", f4808e9f), +_c(U+1003A0, "", f4808ea0), +_c(U+1003A1, "", f4808ea1), +_c(U+1003A2, "", f4808ea2), +_c(U+1003A3, "", f4808ea3), +_c(U+1003A4, "", f4808ea4), +_c(U+1003A5, "", f4808ea5), +_c(U+1003A6, "", f4808ea6), +_c(U+1003A7, "", f4808ea7), +_c(U+1003A8, "", f4808ea8), +_c(U+1003A9, "", f4808ea9), +_c(U+1003AA, "", f4808eaa), +_c(U+1003AB, "", f4808eab), +_c(U+1003AC, "", f4808eac), +_c(U+1003AD, "", f4808ead), +_c(U+1003AE, "", f4808eae), +_c(U+1003AF, "", f4808eaf), +_c(U+1003B0, "", f4808eb0), +_c(U+1003B1, "", f4808eb1), +_c(U+1003B2, "", f4808eb2), +_c(U+1003B3, "", f4808eb3), +_c(U+1003B4, "", f4808eb4), +_c(U+1003B5, "", f4808eb5), +_c(U+1003B6, "", f4808eb6), +_c(U+1003B7, "", f4808eb7), +_c(U+1003B8, "", f4808eb8), +_c(U+1003B9, "", f4808eb9), +_c(U+1003BA, "", f4808eba), +_c(U+1003BB, "", f4808ebb), +_c(U+1003BC, "", f4808ebc), +_c(U+1003BD, "", f4808ebd), +_c(U+1003BE, "", f4808ebe), +_c(U+1003BF, "", f4808ebf), +_c(U+1003C0, "", f4808f80), +_c(U+1003C1, "", f4808f81), +_c(U+1003C2, "", f4808f82), +_c(U+1003C3, "", f4808f83), +_c(U+1003C4, "", f4808f84), +_c(U+1003C5, "", f4808f85), +_c(U+1003C6, "", f4808f86), +_c(U+1003C7, "", f4808f87), +_c(U+1003C8, "", f4808f88), +_c(U+1003C9, "", f4808f89), +_c(U+1003CA, "", f4808f8a), +_c(U+1003CB, "", f4808f8b), +_c(U+1003CC, "", f4808f8c), +_c(U+1003CD, "", f4808f8d), +_c(U+1003CE, "", f4808f8e), +_c(U+1003CF, "", f4808f8f), +_c(U+1003D0, "", f4808f90), +_c(U+1003D1, "", f4808f91), +_c(U+1003D2, "", f4808f92), +_c(U+1003D3, "", f4808f93), +_c(U+1003D4, "", f4808f94), +_c(U+1003D5, "", f4808f95), +_c(U+1003D6, "", f4808f96), +_c(U+1003D7, "", f4808f97), +_c(U+1003D8, "", f4808f98), +_c(U+1003D9, "", f4808f99), +_c(U+1003DA, "", f4808f9a), +_c(U+1003DB, "", f4808f9b), +_c(U+1003DC, "", f4808f9c), +_c(U+1003DD, "", f4808f9d), +_c(U+1003DE, "", f4808f9e), +_c(U+1003DF, "", f4808f9f), +_c(U+1003E0, "", f4808fa0), +_c(U+1003E1, "", f4808fa1), +_c(U+1003E2, "", f4808fa2), +_c(U+1003E3, "", f4808fa3), +_c(U+1003E4, "", f4808fa4), +_c(U+1003E5, "", f4808fa5), +_c(U+1003E6, "", f4808fa6), +_c(U+1003E7, "", f4808fa7), +_c(U+1003E8, "", f4808fa8), +_c(U+1003E9, "", f4808fa9), +_c(U+1003EA, "", f4808faa), +_c(U+1003EB, "", f4808fab), +_c(U+1003EC, "", f4808fac), +_c(U+1003ED, "", f4808fad), +_c(U+1003EE, "", f4808fae), +_c(U+1003EF, "", f4808faf), +_c(U+1003F0, "", f4808fb0), +_c(U+1003F1, "", f4808fb1), +_c(U+1003F2, "", f4808fb2), +_c(U+1003F3, "", f4808fb3), +_c(U+1003F4, "", f4808fb4), +_c(U+1003F5, "", f4808fb5), +_c(U+1003F6, "", f4808fb6), +_c(U+1003F7, "", f4808fb7), +_c(U+1003F8, "", f4808fb8), +_c(U+1003F9, "", f4808fb9), +_c(U+1003FA, "", f4808fba), +_c(U+1003FB, "", f4808fbb), +_c(U+1003FC, "", f4808fbc), +_c(U+1003FD, "", f4808fbd), +_c(U+1003FE, "", f4808fbe), +_c(U+1003FF, "", f4808fbf), +#undef _c diff --git a/thirdparty/ryml/ext/c4core/tools/amalgamate.py b/thirdparty/ryml/ext/c4core/tools/amalgamate.py new file mode 100644 index 000000000..eabcb4891 --- /dev/null +++ b/thirdparty/ryml/ext/c4core/tools/amalgamate.py @@ -0,0 +1,143 @@ +import re +from os.path import abspath, dirname +import sys +import subprocess + +projdir = abspath(dirname(dirname(__file__))) +sys.path.insert(0, f"{projdir}/cmake") +import amalgamate_utils as am + + +def amalgamate_fastfloat(): + fastfloatdir = f"{projdir}/src/c4/ext/fast_float" + subprocess.run([ + sys.executable, + f"{fastfloatdir}/script/amalgamate.py", + "--license", "MIT", + "--output", f"{fastfloatdir}/../fast_float_all.h" + ], cwd=fastfloatdir).check_returncode() + + +def amalgamate_c4core(filename: str, + with_stl: bool=True, + with_fastfloat: bool=True): + if with_fastfloat: + amalgamate_fastfloat() + repo = "https://github.com/biojppm/c4core" + defmacro = "C4CORE_SINGLE_HDR_DEFINE_NOW" + exports_def_code = f"""// shared library: export when defining +#if defined(C4CORE_SHARED) && defined({defmacro}) && !defined(C4CORE_EXPORTS) +#define C4CORE_EXPORTS +#endif +""" + required_gcc4_8_include = """// these includes are needed to work around conditional +// includes in the gcc4.8 shim +#include <cstdint> +#include <type_traits> +#include <cstring> +""" + srcblocks = [ + am.cmttext(f""" +c4core - C++ utilities + +{repo} + +DO NOT EDIT. This file is generated automatically. +This is an amalgamated single-header version of the library. + +INSTRUCTIONS: + - Include at will in any header of your project + - In one (and only one) of your project source files, + #define {defmacro} and then include this header. + This will enable the function and class definitions in + the header file. + - To compile into a shared library, just define the + preprocessor symbol C4CORE_SHARED . This will take + care of symbol export/import. +"""), + am.cmtfile("LICENSE.txt"), + am.injcode(exports_def_code), + "src/c4/export.hpp", + "src/c4/preprocessor.hpp", + "src/c4/platform.hpp", + "src/c4/cpu.hpp", + "src/c4/compiler.hpp", + am.injcode(required_gcc4_8_include), + "cmake/compat/c4/gcc-4.8.hpp", + "src/c4/language.hpp", + "src/c4/types.hpp", + "src/c4/config.hpp", + am.hdrfile("src/c4/ext/debugbreak/debugbreak.h", "c4/ext/debugbreak/debugbreak.h", "DEBUG_BREAK_H"), + "src/c4/error.hpp", + "src/c4/memory_util.hpp", + "src/c4/memory_resource.hpp", + "src/c4/ctor_dtor.hpp", + "src/c4/allocator.hpp", + "src/c4/char_traits.hpp", + "src/c4/hash.hpp", + "src/c4/szconv.hpp", + "src/c4/blob.hpp", + "src/c4/substr_fwd.hpp", + "src/c4/substr.hpp", + am.onlyif(with_fastfloat, am.injfile("src/c4/ext/fast_float_all.h", "c4/ext/fast_float_all.h")), + am.onlyif(with_fastfloat, "src/c4/ext/fast_float.hpp"), + "src/c4/std/vector_fwd.hpp", + "src/c4/std/string_fwd.hpp", + "src/c4/std/std_fwd.hpp", + "src/c4/charconv.hpp", + "src/c4/utf.hpp", + "src/c4/format.hpp", + "src/c4/dump.hpp", + "src/c4/enum.hpp", + "src/c4/bitmask.hpp", + "src/c4/span.hpp", + "src/c4/type_name.hpp", + "src/c4/base64.hpp", + am.onlyif(with_stl, am.ignfile("src/c4/std/std.hpp")), # this is an umbrella include + am.onlyif(with_stl, "src/c4/std/string.hpp"), + am.onlyif(with_stl, "src/c4/std/vector.hpp"), + am.onlyif(with_stl, "src/c4/std/tuple.hpp"), + "src/c4/ext/rng/rng.hpp", + "src/c4/ext/sg14/inplace_function.h", + am.ignfile("src/c4/common.hpp"), + am.ignfile("src/c4/c4_push.hpp"), + am.ignfile("src/c4/c4_pop.hpp"), + am.ignfile("src/c4/restrict.hpp"), + am.ignfile("src/c4/unrestrict.hpp"), + "src/c4/language.cpp", + "src/c4/format.cpp", + "src/c4/memory_util.cpp", + "src/c4/char_traits.cpp", + "src/c4/memory_resource.cpp", + "src/c4/utf.cpp", + "src/c4/base64.cpp", + am.injcode("#define C4_WINDOWS_POP_HPP_"), + "src/c4/windows_push.hpp", + "src/c4/windows.hpp", + "src/c4/windows_pop.hpp", # do NOT include this before windows.hpp + "src/c4/error.cpp", + ] + result = am.catfiles(srcblocks, + projdir, + # comment out lines with these patterns: + include_regexes=[ + re.compile(r'^\s*#\s*include "(c4/.*)".*$'), + re.compile(r'^\s*#\s*include <(c4/.*)>.*$'), + ], + definition_macro=defmacro, + repo=repo, + result_incguard="_C4CORE_SINGLE_HEADER_AMALGAMATED_HPP_") + result_with_only_first_includes = am.include_only_first(result) + am.file_put_contents(filename, result_with_only_first_includes) + + +def mkparser(): + return am.mkparser(fastfloat=(True, "enable fastfloat library"), + stl=(True, "enable stl interop")) + + +if __name__ == "__main__": + args = mkparser().parse_args() + amalgamate_c4core(filename=args.output, + with_fastfloat=args.fastfloat, + with_stl=args.stl) |