aboutsummaryrefslogtreecommitdiff
path: root/thirdparty/ryml/ext/c4core
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2025-11-07 14:49:13 +0100
committerGitHub Enterprise <[email protected]>2025-11-07 14:49:13 +0100
commit24e43a913f29ac3b314354e8ce5175f135bcc64f (patch)
treeca442937ceeb63461012b33a4576e9835099f106 /thirdparty/ryml/ext/c4core
parentget oplog attachments (#622) (diff)
downloadzen-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')
-rw-r--r--thirdparty/ryml/ext/c4core/.github/release.sh129
-rw-r--r--thirdparty/ryml/ext/c4core/.github/reqs.sh314
-rw-r--r--thirdparty/ryml/ext/c4core/.github/setenv.sh443
-rw-r--r--thirdparty/ryml/ext/c4core/.github/vagrant/Vagrantfile80
-rw-r--r--thirdparty/ryml/ext/c4core/.github/vagrant/macos/Vagrantfile71
-rwxr-xr-xthirdparty/ryml/ext/c4core/.github/vagrant/vagrant-provision.sh71
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/arch.yml116
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/benchmarks.yml250
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/clang.yml230
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/clang_tidy.yml97
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/codeql.yml43
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/coverage.yml150
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/emscripten.yml99
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/gcc.yml181
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/libcxx.yml111
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/macosx.yml103
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/release.yml197
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/test_install.yml104
-rw-r--r--thirdparty/ryml/ext/c4core/.github/workflows/windows.yml157
-rw-r--r--thirdparty/ryml/ext/c4core/.gitignore34
-rw-r--r--thirdparty/ryml/ext/c4core/.gitmodules9
-rw-r--r--thirdparty/ryml/ext/c4core/LICENSE-BOOST.txt26
-rw-r--r--thirdparty/ryml/ext/c4core/LICENSE.txt20
-rw-r--r--thirdparty/ryml/ext/c4core/README.md381
-rw-r--r--thirdparty/ryml/ext/c4core/ROADMAP.md23
-rw-r--r--thirdparty/ryml/ext/c4core/bm/bm.yml32
-rw-r--r--thirdparty/ryml/ext/c4core/bm/bm_atox.cpp478
-rw-r--r--thirdparty/ryml/ext/c4core/bm/bm_charconv.cpp1617
-rw-r--r--thirdparty/ryml/ext/c4core/bm/bm_charconv.hpp454
-rw-r--r--thirdparty/ryml/ext/c4core/bm/bm_format.cpp900
-rw-r--r--thirdparty/ryml/ext/c4core/bm/bm_itoa_threads.cpp356
-rw-r--r--thirdparty/ryml/ext/c4core/bm/bm_plot_c4core.py266
-rw-r--r--thirdparty/ryml/ext/c4core/bm/bm_xtoa.cpp1538
-rw-r--r--thirdparty/ryml/ext/c4core/bm/float/measure.py65
-rw-r--r--thirdparty/ryml/ext/c4core/bm/float/read.cpp170
-rw-r--r--thirdparty/ryml/ext/c4core/bm/ryu.cmake37
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.0.md3
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.1.md5
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.10.md106
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.11.md59
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.2.md4
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.3.md1
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.4.md6
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.5.md2
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.6.md2
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.7.md5
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.8.md45
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/0.1.9.md31
-rw-r--r--thirdparty/ryml/ext/c4core/changelog/current.md0
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/.gitignore1
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/ConfigurationTypes.cmake120
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/CreateSourceGroup.cmake31
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/Doxyfile.full.in2566
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/Doxyfile.in2566
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/ExternalProjectUtils.cmake215
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/FindD3D12.cmake75
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/FindDX12.cmake76
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/GetFlags.cmake53
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/GetNames.cmake51
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/LICENSE.txt20
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/PVS-Studio.cmake275
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/PatchUtils.cmake25
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/PrintVar.cmake27
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/README.md25
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/TargetArchitecture.cmake180
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/Toolchain-Arm-ubuntu.cmake29
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/Toolchain-Armv7.cmake84
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/Toolchain-PS4.cmake73
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/Toolchain-XBoxOne.cmake93
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/amalgamate_utils.py219
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/.gitignore1
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/README.md7
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/bm.js475
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_plot.py746
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_run.py248
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_serve.py502
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/bm_util.py147
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/example_c4core.py1061
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/example_mintm.py1061
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/requirements.txt14
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/bm-xp/template/index.html45
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/c4CatSources.cmake105
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/c4Doxygen.cmake121
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/c4DoxygenConfig.cmake24
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/c4GetTargetPropertyRecursive.cmake186
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/c4Project.cmake3691
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/c4SanitizeTarget.cmake292
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/c4StaticAnalysis.cmake154
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/c4stlAddTarget.cmake5
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/compat/c4/gcc-4.8.hpp69
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/compat/gtest_gcc-4.8.patch97
-rw-r--r--thirdparty/ryml/ext/c4core/cmake/requirements_doc.txt3
-rw-r--r--thirdparty/ryml/ext/c4core/compat.cmake16
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/allocator.hpp405
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/base64.cpp220
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/base64.hpp98
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/bitmask.hpp330
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/blob.hpp50
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/c4_pop.hpp19
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/c4_push.hpp37
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/c4core.natvis168
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/char_traits.cpp10
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/char_traits.hpp98
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/charconv.hpp2407
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/common.hpp15
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/compiler.hpp117
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/config.hpp39
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/cpu.hpp139
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ctor_dtor.hpp462
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/dump.hpp579
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/enum.hpp276
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/error.cpp227
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/error.hpp432
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/export.hpp18
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/.gitignore10
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/COPYING23
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/GNUmakefile28
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/HOW-TO-USE-DEBUGBREAK-GDB-PY.md33
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/README.md127
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/debugbreak-gdb.py183
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/debugbreak.h174
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break-c++.cc9
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break.c9
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/break.gdb2
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/fib.c21
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/fib.gdb2
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/test-debugbreak.gdb6
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/trap.c8
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/debugbreak/test/trap.gdb3
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float.hpp28
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.cirrus.yml22
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/alpine.yml27
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/amalgamate-ubuntu20.yml25
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/msys2-clang.yml39
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/msys2.yml45
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu18.yml35
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu20-cxx20.yml19
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/ubuntu20.yml29
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs15-ci.yml29
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-arm-ci.yml21
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-ci.yml29
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-clang-ci.yml27
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.github/workflows/vs16-cxx20.yml24
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.gitignore4
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/.travis.yml242
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/AUTHORS2
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/CONTRIBUTORS6
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/LICENSE-APACHE201
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/LICENSE-MIT23
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/README.md216
-rwxr-xr-xthirdparty/ryml/ext/c4core/src/c4/ext/fast_float/ci/script.sh18
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/cmake/config.cmake.in4
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/ascii_number.h231
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/bigint.h590
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/decimal_to_binary.h194
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/digit_comparison.h423
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/fast_float.h63
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/fast_table.h699
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/float_common.h362
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/parse_number.h113
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/include/fast_float/simple_decimal_conversion.h360
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/amalgamate.py56
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/analysis.py36
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/script/table_generation.py31
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/basictest.cpp704
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/foo.cpp2
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/main.cpp2
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/build_tests/issue72/test.h2
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/example_comma_test.cpp15
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/example_test.cpp14
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32.cpp63
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32_64.cpp78
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/exhaustive32_midpoint.cpp148
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_exhaustive32.cpp63
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_exhaustive32_64.cpp66
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_random64.cpp106
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/long_test.cpp58
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/powersoffive_hardround.cpp134
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/random64.cpp109
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/random_string.cpp240
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/short_random_string.cpp234
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float/tests/string_test.cpp279
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/fast_float_all.h2947
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/rng/rng.hpp192
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/sg14/README.md1
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/ext/sg14/inplace_function.h347
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/format.cpp56
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/format.hpp900
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/hash.hpp95
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/language.cpp16
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/language.hpp275
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/memory_resource.cpp338
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/memory_resource.hpp568
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/memory_util.cpp30
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/memory_util.hpp774
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/platform.hpp44
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/preprocessor.hpp123
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/restrict.hpp51
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/span.hpp517
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/std/std.hpp10
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/std/std_fwd.hpp10
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/std/string.hpp97
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/std/string_fwd.hpp56
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/std/tuple.hpp184
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/std/vector.hpp88
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/std/vector_fwd.hpp60
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/substr.hpp2246
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/substr_fwd.hpp16
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/szconv.hpp64
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/type_name.hpp125
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/types.hpp492
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/unrestrict.hpp17
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/utf.cpp56
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/utf.hpp16
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/windows.hpp10
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/windows_pop.hpp41
-rw-r--r--thirdparty/ryml/ext/c4core/src/c4/windows_push.hpp102
-rw-r--r--thirdparty/ryml/ext/c4core/tbump.toml48
-rw-r--r--thirdparty/ryml/ext/c4core/test/c4/libtest/archetypes.cpp9
-rw-r--r--thirdparty/ryml/ext/c4core/test/c4/libtest/archetypes.hpp551
-rw-r--r--thirdparty/ryml/ext/c4core/test/c4/libtest/supprwarn_pop.hpp12
-rw-r--r--thirdparty/ryml/ext/c4core/test/c4/libtest/supprwarn_push.hpp48
-rw-r--r--thirdparty/ryml/ext/c4core/test/c4/libtest/test.cpp7
-rw-r--r--thirdparty/ryml/ext/c4core/test/c4/main.cpp3
-rw-r--r--thirdparty/ryml/ext/c4core/test/c4/test.hpp324
-rw-r--r--thirdparty/ryml/ext/c4core/test/printintegers.py107
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_allocator.cpp246
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_base64.cpp265
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_bitmask.cpp385
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_blob.cpp43
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_char_traits.cpp67
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_charconv.cpp2743
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_ctor_dtor.cpp306
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_dump.cpp1220
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_enum.cpp158
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_enum_common.hpp213
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_error.cpp635
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_error_exception.cpp108
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_format.cpp1054
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_log.cpp70
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_memory_resource.cpp255
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_memory_util.cpp415
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_numbers.hpp1863
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_preprocessor.cpp55
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_singleheader/libc4core_singleheader.cpp2
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_span.cpp944
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_std_string.cpp125
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_std_vector.cpp133
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_substr.cpp4507
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_szconv.cpp166
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_type_name.cpp49
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_types.cpp81
-rw-r--r--thirdparty/ryml/ext/c4core/test/test_utf.cpp48
-rw-r--r--thirdparty/ryml/ext/c4core/test/utfchars.inc6274
-rw-r--r--thirdparty/ryml/ext/c4core/tools/amalgamate.py143
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
+
+[![MIT Licensed](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/biojppm/c4core/blob/master/LICENSE.txt)
+[![Docs](https://img.shields.io/badge/docs-docsforge-blue)](https://c4core.docsforge.com/)
+[![ci](https://github.com/biojppm/c4core/workflows/ci/badge.svg)](https://github.com/biojppm/c4core/actions?query=ci)
+<!-- [![Coveralls](https://coveralls.io/repos/github/biojppm/c4core/badge.svg)](https://coveralls.io/github/biojppm/c4core) -->
+[![Codecov](https://codecov.io/gh/biojppm/c4core/branch/master/graph/badge.svg)](https://codecov.io/gh/biojppm/c4core)
+[![LGTM alerts](https://img.shields.io/lgtm/alerts/g/biojppm/c4core.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/biojppm/c4core/alerts/)
+[![LGTM grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/biojppm/c4core.svg?logo=lgtm&logoWidth=18)](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 |
+|---------------|----------------|
+| ![atox-u32-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-atox-mega_bytes_per_second-u32.png "atox-u32-linux") | ![atox-u32-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-atox-mega_bytes_per_second-u32.png "atox-u32-windows") |
+| ![atox-i32-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-atox-mega_bytes_per_second-i32.png "atox-i32-linux") | ![atox-i32-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-atox-mega_bytes_per_second-i32.png "atox-i32-windows") |
+| ![atox-u64-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-atox-mega_bytes_per_second-u64.png "atox-u64-linux") | ![atox-u64-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-atox-mega_bytes_per_second-u64.png "atox-u64-windows") |
+| ![atox-i64-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-atox-mega_bytes_per_second-i64.png "atox-i64-linux") | ![atox-i64-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-atox-mega_bytes_per_second-i64.png "atox-i64-windows") |
+| ![atof-double-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-atof-mega_bytes_per_second-double.png "atof-double-linux") | ![atof-double-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-atof-mega_bytes_per_second-double.png "atof-double-windows") |
+| ![atof-float-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-atof-mega_bytes_per_second-float.png "atof-float-linux") | ![atof-float-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-atof-mega_bytes_per_second-float.png "atof-float-windows") |
+| | |
+| ![xtoa-u32-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-xtoa-mega_bytes_per_second-u32.png "xtoa-u32-linux") | ![xtoa-u32-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-xtoa-mega_bytes_per_second-u32.png "xtoa-u32-windows") |
+| ![xtoa-i32-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-xtoa-mega_bytes_per_second-i32.png "xtoa-i32-linux") | ![xtoa-i32-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-xtoa-mega_bytes_per_second-i32.png "xtoa-i32-windows") |
+| ![xtoa-u64-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-xtoa-mega_bytes_per_second-u64.png "xtoa-u64-linux") | ![xtoa-u64-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-xtoa-mega_bytes_per_second-u64.png "xtoa-u64-windows") |
+| ![xtoa-i64-linux](doc/img/linux-x86_64-gxx12.1-Release/c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png "xtoa-i64-linux") | ![xtoa-i64-windows](doc/img/windows-x86_64-vs2019-Release/c4core-bm-charconv-xtoa-mega_bytes_per_second-i64.png "xtoa-i64-windows") |
+
+
+
+### 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)
+
+[![{data_type}: MB/s](./{pfx_bps}.png)](./{pfx_bps}.png)
+[![{data_type}: CPU time](./{pfx_cpu}.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("&amp;", "&")
+ .replace("&lt;", "<")
+ .replace("&gt;", ">")
+ .replace("&quot;", "\"")
+ .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, "&amp;")
+ .replace(/</g, "&lt;")
+ .replace(/>/g, "&gt;")
+ .replace(/"/g, "&quot;")
+ .replace(/'/g, "&#039;");
+}
+
+
+/* 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)
+
+
+def home():
+ log("requested home")
+ return render_template("index.html")
+
+
+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)
+
+
+def home():
+ log("requested home")
+ return render_template("index.html")
+
+
+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)
+
+
+def home():
+ log("requested home")
+ return render_template("index.html")
+
+
+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&lt;*&gt;">
+ <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&lt;*&gt;">
+ <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&lt;*&gt;">
+ <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&lt;char,*&gt;">
+ <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&lt;const char,*&gt;">
+ <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&lt;char,*&gt;">
+ <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&lt;const char,*&gt;">
+ <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&lt;*,*,*,*&gt;">
+ <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&lt;*,*&gt;">
+ <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&lt;*,*&gt;">
+ <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&lt;*,*,*&gt;">
+ <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&lt;*&gt;::Sym">
+ <DisplayString>{value} - {name}</DisplayString>
+ <Expand>
+ <Item Name="[value]">value</Item>
+ <Item Name="[name]">name</Item>
+ </Expand>
+ </Type>
+ <Type Name="c4::EnumSymbols&lt;*&gt;">
+ <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
+
+![Ubuntu 20.04 CI (GCC 9)](https://github.com/lemire/fast_float/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)
+![Ubuntu 18.04 CI (GCC 7)](https://github.com/lemire/fast_float/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg)
+![Alpine Linux](https://github.com/lemire/fast_float/workflows/Alpine%20Linux/badge.svg)
+![MSYS2-CI](https://github.com/lemire/fast_float/workflows/MSYS2-CI/badge.svg)
+![VS16-CLANG-CI](https://github.com/lemire/fast_float/workflows/VS16-CLANG-CI/badge.svg)
+[![VS16-CI](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml/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
+
+[![Go Systems 2020](http://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](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, &copy);
+ 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, &copy);
+ 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)