[
  {
    "path": ".clang-format",
    "content": "# Run manually to reformat a file:\n# clang-format -i --style=file <file>\nLanguage: Cpp\nBasedOnStyle: Google\nIndentPPDirectives: AfterHash\nIndentCaseLabels: false\nDerivePointerAlignment: false\nStandard: c++20\nColumnLimit: 120\nAllowShortFunctionsOnASingleLine: Empty\nAllowShortLambdasOnASingleLine: None\nIndentRequiresClause: true\n"
  },
  {
    "path": ".clang-tidy",
    "content": "---\n# Enable ALL the things! Except not really\n# cppcoreguidelines-avoid-magic-numbers/readability-magic-numbers: covers to many simply cases\n# modernize-use-trailing-return-type: purely stylistic suggestion\n# readability-identifier-length: not needed\n# bugprone-easily-swappable-parameters: covers to many simple cases\n# cppcoreguidelines-macro-usage: reduced to a minimum\n# bugprone-macro-parentheses: false positive\n# readability-function-cognitive-complexity: multiple if/else branches necessary to cover all use cases\n# cppcoreguidelines-non-private-member-variables-in-classes: same as misc-non-private-member-variables-in-classes\n# misc-non-private-member-variables-in-classes: allowed since classes are intended for single threading\n# cppcoreguidelines-pro-type-union-access: allowed since it is used because of performance and compile-time reasons\n# readability-else-after-return: allowed since if no else compile-time flow check fails and no optimization in debug\n# bugprone-exception-escape: to many false positives\n# cppcoreguidelines-pro-bounds-pointer-arithmetic: performance in debug\n# cppcoreguidelines-pro-bounds-constant-array-index:: performance\nChecks: >\n  bugprone-*,\n  concurrency-*,\n  cppcoreguidelines-*,\n  clang-analyzer-*,\n  misc-*,\n  modernize-*,\n  performance-*,\n  portability-*,\n  readability-*,\n  -clang-analyzer-osx*,\n  -clang-analyzer-llvm*,\n  -clang-analyzer-optin*,\n  -clang-analyzer-unix*,\n  -clang-analyzer-valist*,\n  -clang-diagnostic-ignored-optimization-argument,\n  -cppcoreguidelines-avoid-magic-numbers,\n  -readability-magic-numbers,\n  -modernize-use-trailing-return-type,\n  -readability-identifier-length,\n  -bugprone-easily-swappable-parameters,\n  -cppcoreguidelines-macro-usage,\n  -bugprone-macro-parentheses,\n  -readability-function-cognitive-complexity,\n  -cppcoreguidelines-non-private-member-variables-in-classes,\n  -misc-non-private-member-variables-in-classes,\n  -cppcoreguidelines-pro-type-union-access,\n  -readability-else-after-return,\n  -bugprone-exception-escape,\n  -cppcoreguidelines-pro-bounds-pointer-arithmetic,\n  -cppcoreguidelines-pro-bounds-constant-array-index,\n  -cppcoreguidelines-avoid-do-while,\n  -cppcoreguidelines-avoid-const-or-ref-data-members,\n  -misc-include-cleaner,\n  \n\nWarningsAsErrors: ''\n\nCheckOptions:\n  - key: readability-identifier-naming.AbstractClassCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ClassCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ClassConstantCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ClassMemberCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ClassMethodCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ConstantCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ConstantParameterCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ConstantPointerParameterCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ConstexprFunctionCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ConstexprMethodCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ConstexprVariableCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.EnumCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.EnumConstantCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.FunctionCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.GlobalConstantCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.GlobalConstantPointerCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.GlobalFunctionCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.GlobalPointerCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.GlobalVariableCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.InlineNamespaceCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.LocalConstantCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.LocalConstantPointerCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.LocalPointerCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.LocalVariableCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.MacroDefinitionCase\n    value: 'UPPER_CASE'\n  - key: readability-identifier-naming.MemberCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.MethodCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.NamespaceCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ParameterCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ParameterPackCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.PointerParameterCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.PrivateMemberCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.PrivateMemberSuffix\n    value: '_'\n  - key: readability-identifier-naming.PrivateMethodCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ProtectedMemberCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ProtectedMemberSuffix\n    value: '_'\n  - key: readability-identifier-naming.ProtectedMethodCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.PublicMemberCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.PublicMethodCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ScopedEnumConstantCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.StaticConstantCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.StaticVariableCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.StructCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.TemplateParameterCase\n    value: 'CamelCase'\n  - key: readability-identifier-naming.TypeTemplateParameterIgnoredRegexp\n    value: 'expr-type' # see https://github.com/llvm/llvm-project/issues/46097\n  - key: readability-identifier-naming.TemplateTemplateParameterCase\n    value: 'CamelCase'\n  - key: readability-identifier-naming.TypeAliasCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.TypedefCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.TypeTemplateParameterCase\n    value: 'CamelCase'\n  - key: readability-identifier-naming.UnionCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.ValueTemplateParameterCase\n    value: 'CamelCase'\n  - key: readability-identifier-naming.VariableCase\n    value: 'lower_case'\n  - key: readability-identifier-naming.VirtualMethodCase\n    value: 'lower_case'\n...\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: Continuous Integration\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\njobs:\n  checks:\n    runs-on: ubuntu-22.04\n\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Install GCC\n        run: |\n          sudo apt update && sudo apt install -y gcc-11 g++-11\n          echo \"CC=gcc-11\" >> $GITHUB_ENV\n          echo \"CXX=g++-11\" >> $GITHUB_ENV\n\n      - name: Install cppcheck\n        run: |\n          sudo apt update && sudo apt install -y cppcheck\n          cppcheck --version\n\n      - name: Install clang-format and clang-tidy\n        run: |\n          wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -\n          sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main'\n          sudo apt update && sudo apt install -y clang-format-17 clang-tidy-17\n          sudo update-alternatives --remove-all clang-format || true\n          sudo update-alternatives --remove-all clang-tidy || true\n          sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-17 1000\n          sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-17 1000\n          clang-tidy --version\n          clang-format --version\n\n      - name: Run clang-format\n        run: cmake -D FORMAT_COMMAND=clang-format -P cmake/lint.cmake\n\n      - name: Run cppcheck and clang-tidy\n        # Cppcheck's checks are disabled. They are mostly redundant to clang-tidy and there are too many false positives.\n        # But the parsing of cppcheck is kept to be compatible with its compiler frontend.\n        if: ${{ !cancelled() }}\n        run: |\n          cmake --preset=ci-checks\n          cmake --build build/test/static_analysis -j $(nproc)\n\n  unit-tests:\n    strategy:\n      matrix:\n        env: [ {os: ubuntu-22.04, compiler: gcc-11, std: 20},\n               {os: ubuntu-22.04, compiler: gcc-12, std: 20},\n               {os: ubuntu-22.04, compiler: gcc-12, std: 23},\n               {os: ubuntu-24.04, compiler: gcc-13, std: 20},\n               {os: ubuntu-24.04, compiler: gcc-13, std: 23},\n               {os: ubuntu-24.04, compiler: gcc-14, std: 20},\n               {os: ubuntu-24.04, compiler: gcc-14, std: 23},\n               {os: ubuntu-22.04, compiler: clang-16, std: 20},\n               {os: ubuntu-22.04, compiler: clang-16, std: 23},\n               {os: ubuntu-22.04, compiler: clang-17, std: 20},\n               {os: ubuntu-22.04, compiler: clang-17, std: 23},\n               {os: ubuntu-24.04, compiler: clang-18, std: 20},\n               {os: ubuntu-24.04, compiler: clang-18, std: 23},\n               {os: ubuntu-24.04, compiler: clang-19, std: 20},\n               {os: ubuntu-24.04, compiler: clang-19, std: 23}\n             ]\n        build_type: [ Debug, Release ]\n\n    runs-on: ${{ matrix.env.os }}\n\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Install Compiler\n        run: |\n          if [[ \"$compiler\" == \"gcc-11\" ]]; then\n            sudo apt update && sudo apt install -y gcc-11 g++-11\n            echo \"CC=gcc-11\" >> $GITHUB_ENV\n            echo \"CXX=g++-11\" >> $GITHUB_ENV\n          elif [[ \"$compiler\" == \"gcc-12\" ]]; then\n            sudo apt update && sudo apt install -y gcc-12 g++-12\n            echo \"CC=gcc-12\" >> $GITHUB_ENV\n            echo \"CXX=g++-12\" >> $GITHUB_ENV\n          elif [[ \"$compiler\" == \"gcc-13\" ]]; then\n            sudo apt update && sudo apt install -y gcc-13 g++-13\n            echo \"CC=gcc-13\" >> $GITHUB_ENV\n            echo \"CXX=g++-13\" >> $GITHUB_ENV\n          elif [[ \"$compiler\" == \"clang-16\" ]]; then\n            wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -\n            sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main'\n            sudo apt update && sudo apt install -y clang-16\n            echo \"CC=clang-16\" >> $GITHUB_ENV\n            echo \"CXX=clang++-16\" >> $GITHUB_ENV\n          elif [[ \"$compiler\" == \"clang-17\" ]]; then\n            wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -\n            sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main'\n            sudo apt update && sudo apt install -y clang-17\n            echo \"CC=clang-17\" >> $GITHUB_ENV\n            echo \"CXX=clang++-17\" >> $GITHUB_ENV\n          elif [[ \"$compiler\" == \"clang-18\" ]]; then\n            wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -\n            sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-18 main'\n            sudo apt update && sudo apt install -y clang-18\n            echo \"CC=clang-18\" >> $GITHUB_ENV\n            echo \"CXX=clang++-18\" >> $GITHUB_ENV\n          elif [[ \"$compiler\" == \"clang-20\" ]]; then\n            wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -\n            sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main'\n            sudo apt update && sudo apt install -y clang-20\n            echo \"CC=clang-20\" >> $GITHUB_ENV\n            echo \"CXX=clang++-20\" >> $GITHUB_ENV\n          fi\n        env:\n          compiler: ${{ matrix.env.compiler }}\n\n      - name: Configure\n        run: cmake --preset=ci-ubuntu -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_CXX_STANDARD=${{ matrix.env.std }}\n\n      - name: Build Catch2\n        run: cmake --build build -t Catch2WithMain -j $(nproc)\n\n      - name: Build\n        run: cmake --build build -j $(nproc)\n\n      - name: Install\n        run: cmake --install build --prefix prefix\n\n      - name: Test\n        working-directory: build\n        run: ctest --verbose --output-on-failure\n\n  coverage:\n    runs-on: ubuntu-22.04\n\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Install GCC\n        run: |\n          sudo apt update && sudo apt install -y gcc-11 g++-11\n          echo \"CC=gcc-11\" >> $GITHUB_ENV\n          echo \"CXX=g++-11\" >> $GITHUB_ENV\n\n      - name: Install LCov\n        run: sudo apt update && sudo apt install lcov -q -y\n\n      - name: Configure\n        run: cmake --preset=ci-coverage\n\n      - name: Build Catch2\n        run: cmake --build build -t Catch2WithMain -j $(nproc)\n\n      - name: Build\n        run: cmake --build build/test/unit_test -j $(nproc)\n\n      - name: Test\n        working-directory: build/test/unit_test\n        run: ctest --output-on-failure\n\n      - name: Process coverage info\n        run: cmake --build build -t coverage\n\n      - name: Submit to codecov.io\n        uses: codecov/codecov-action@v3\n        with:\n          files: build/coverage.info\n\n  memcheck:\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Install GCC\n        run: |\n          sudo apt update && sudo apt install -y gcc-14 g++-14\n          echo \"CC=gcc-14\" >> $GITHUB_ENV\n          echo \"CXX=g++-14\" >> $GITHUB_ENV\n\n      - name: Install valgrind\n        run: sudo apt update && sudo apt install -y valgrind\n\n      - name: Configure\n        run: cmake --preset=ci-memcheck\n\n      - name: Build Catch2\n        run: cmake --build build -t Catch2WithMain -j $(nproc)\n\n      - name: Build\n        run: cmake --build build -j $(nproc)\n\n      - name: Install\n        run: cmake --install build --prefix prefix\n\n      - name: Test\n        working-directory: build\n        run: |\n          if ! ctest --verbose --output-on-failure -T memcheck; then\n            find Testing/Temporary -name \"MemoryChecker.*.log\" -exec cat {} +\n            exit 1\n          fi\n\n  sanitize:\n    runs-on: ubuntu-24.04\n\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Install GCC\n        run: |\n          sudo apt update && sudo apt install -y gcc-14 g++-14\n          echo \"CC=gcc-14\" >> $GITHUB_ENV\n          echo \"CXX=g++-14\" >> $GITHUB_ENV\n\n      - name: Configure\n        run: cmake --preset=ci-sanitize\n\n      - name: Build Catch2\n        run: cmake --build build -t Catch2WithMain -j $(nproc)\n\n      - name: Build\n        run: cmake --build build/test/unit_test -j $(nproc)\n\n      - name: Test\n        working-directory: build/test/unit_test\n        env:\n          ASAN_OPTIONS: \"strict_string_checks=1:\\\n          detect_stack_use_after_return=1:\\\n          check_initialization_order=1:\\\n          strict_init_order=1:\\\n          detect_leaks=1\"\n          UBSAN_OPTIONS: print_stacktrace=1\n        run: ctest --output-on-failure\n\n  size-test:\n    runs-on: ubuntu-22.04\n\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Install GCC\n        run: |\n          sudo apt update && sudo apt install -y gcc-11 g++-11\n          echo \"CC=gcc-11\" >> $GITHUB_ENV\n          echo \"CXX=g++-11\" >> $GITHUB_ENV\n\n      - name: Configure\n        run: cmake --preset=ci-size-coverage -DCMAKE_BUILD_TYPE=MinSizeRel\n\n      - name: Build\n        run: cmake --build build/test/size_test -j $(nproc)\n\n      - name: Size-Coverage\n        run: cmake --build build -t size-coverage\n\n  size-test-embedded:\n    strategy:\n      matrix:\n        preset: [ ci-size-coverage-embedded, ci-size-coverage-embedded-nano ]\n\n    runs-on: ubuntu-22.04\n\n    env:\n      DOWNLOAD_LINK: https://developer.arm.com/-/media/Files/downloads/gnu/11.3.rel1/binrel/arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi.tar.xz\n\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Cache toolchain\n        id: cache-toolchain\n        uses: actions/cache@v4\n        with:\n          path: arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi\n          key: ${{ env.DOWNLOAD_LINK }}\n\n      - name: Install GCC ARM none eabi\n        if: steps.cache-toolchain.outputs.cache-hit != 'true'\n        run: |\n          curl -L $DOWNLOAD_LINK | tar xJ\n\n      - name: Configure\n        run: cmake --preset=${{ matrix.preset }} -DCMAKE_BUILD_TYPE=MinSizeRel\n\n      - name: Build\n        run: cmake --build build/test/size_test -j $(nproc)\n\n      - name: Size-Coverage\n        run: cmake --build build -t size-coverage\n\n#      - name: Coveralls - Doesn't work.\n#        uses: coverallsapp/github-action@master\n#        with:\n#          github-token: ${{ secrets.GITHUB_TOKEN }}\n#          path-to-lcov: build/test/size_test/size-coverage.info\n"
  },
  {
    "path": ".github/workflows/pages.yml",
    "content": "name: Deploy Github Pages\n\non:\n  push:\n    branches:\n      - main\n    tags:\n      - \"[0-9]+.[0-9]+.[0-9]+\"\n\njobs:\n  deploy-pages:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v3\n\n      - name: Generate single header file\n        run: |\n          pip install quom\n          if [[ -z \"$(git tag --points-at HEAD)\" ]]; then\n            DST=trunk\n          else\n            DST=$(git tag)\n          fi\n          mkdir -p web/dist/$DST\n          quom include/emio/emio.hpp web/dist/$DST/emio.hpp\n\n      - name: Deploy\n        uses: JamesIves/github-pages-deploy-action@v4\n        with:\n          branch: gh-pages\n          folder: web/\n          clean: false\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\n.vs/\n.vscode/\nbuild/\ncmake/open-cpp-coverage.cmake\ncmake-build-*/\nprefix/\nCMakeLists.txt.user\nCMakeUserPresets.json\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\n\ninclude(cmake/prelude.cmake)\n\nproject(\n        emio\n        VERSION 0.2.0\n        DESCRIPTION \"Character input/output library for embedded systems.\"\n        HOMEPAGE_URL \"https://github.com/viatorus/emio\"\n        LANGUAGES CXX\n)\n\ninclude(cmake/project-is-top-level.cmake)\ninclude(cmake/variables.cmake)\n\n# ---- Declare library ----\n\nadd_library(emio_emio INTERFACE)\nadd_library(emio::emio ALIAS emio_emio)\n\nset_property(\n        TARGET emio_emio PROPERTY\n        EXPORT_NAME emio\n)\n\ntarget_include_directories(\n        emio_emio ${warning_guard}\n        INTERFACE\n        \"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>\"\n)\n\ntarget_compile_features(emio_emio INTERFACE cxx_std_20)\n\n# ---- Install rules ----\n\nif (NOT CMAKE_SKIP_INSTALL_RULES)\n    include(cmake/install-rules.cmake)\nendif ()\n\n# ---- Developer mode ----\n\nif (NOT emio_DEVELOPER_MODE)\n    return()\nelseif (NOT PROJECT_IS_TOP_LEVEL)\n    message(\n            AUTHOR_WARNING\n            \"Developer mode is intended for developers of emio\"\n    )\nendif ()\n\ninclude(cmake/dev-mode.cmake)\n"
  },
  {
    "path": "CMakePresets.json",
    "content": "{\n  \"version\": 2,\n  \"cmakeMinimumRequired\": {\n    \"major\": 3,\n    \"minor\": 14,\n    \"patch\": 0\n  },\n  \"configurePresets\": [\n    {\n      \"name\": \"cmake-pedantic\",\n      \"hidden\": true,\n      \"warnings\": {\n        \"dev\": true,\n        \"deprecated\": true,\n        \"uninitialized\": true,\n        \"unusedCli\": true,\n        \"systemVars\": false\n      },\n      \"errors\": {\n        \"dev\": true,\n        \"deprecated\": true\n      }\n    },\n    {\n      \"name\": \"dev-mode\",\n      \"hidden\": true,\n      \"inherits\": \"cmake-pedantic\",\n      \"cacheVariables\": {\n        \"emio_DEVELOPER_MODE\": \"ON\"\n      }\n    },\n    {\n      \"name\": \"cppcheck\",\n      \"hidden\": true,\n      \"cacheVariables\": {\n        \"CMAKE_CXX_CPPCHECK\": \"cppcheck;--error-exitcode=13\"\n      }\n    },\n    {\n      \"name\": \"clang-tidy\",\n      \"hidden\": true,\n      \"cacheVariables\": {\n        \"CMAKE_CXX_CLANG_TIDY\": \"clang-tidy;--header-filter=${sourceDir}/*;-warnings-as-errors=*\"\n      }\n    },\n    {\n      \"name\": \"valgrind\",\n      \"hidden\": true,\n      \"cacheVariables\": {\n        \"MEMORYCHECK_COMMAND_OPTIONS\": \"--error-exitcode=1 --leak-check=full\"\n      }\n    },\n    {\n      \"name\": \"ci-std\",\n      \"description\": \"This preset makes sure the project actually builds with at least the specified standard\",\n      \"hidden\": true,\n      \"cacheVariables\": {\n        \"CMAKE_CXX_EXTENSIONS\": \"OFF\",\n        \"CMAKE_CXX_STANDARD\": \"23\",\n        \"CMAKE_CXX_STANDARD_REQUIRED\": \"ON\"\n      }\n    },\n    {\n      \"name\": \"flags-unix\",\n      \"hidden\": true,\n      \"environment\": {\n        \"CXX_FLAGS\": \"-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wshadow -Wformat=2 -Wundef\"\n      }\n    },\n    {\n      \"name\": \"ci-unix\",\n      \"generator\": \"Unix Makefiles\",\n      \"hidden\": true,\n      \"inherits\": [\n        \"flags-unix\",\n        \"ci-std\"\n      ],\n      \"cacheVariables\": {\n        \"CMAKE_CXX_FLAGS\": \"$env{CXX_FLAGS}\",\n        \"CMAKE_BUILD_TYPE\": \"Release\"\n      }\n    },\n    {\n      \"name\": \"ci-arm-none-eabi\",\n      \"generator\": \"Unix Makefiles\",\n      \"hidden\": true,\n      \"inherits\": [\n        \"flags-unix\",\n        \"ci-std\"\n      ],\n      \"cacheVariables\": {\n        \"CMAKE_TOOLCHAIN_FILE\": \"${sourceDir}/cmake/arm-none-eabi-toolchain.cmake\",\n        \"CMAKE_CXX_FLAGS\": \"$env{CXX_FLAGS} -ffunction-sections -fdata-sections -mcpu=cortex-m7 -mthumb -g0 -fno-exceptions -fno-rtti\",\n        \"CMAKE_EXE_LINKER_FLAGS\": \"-mcpu=cortex-m7 -mthumb -Wl,--gc-sections\",\n        \"CMAKE_BUILD_TYPE\": \"MinSizeRel\"\n      }\n    },\n    {\n      \"name\": \"ci-arm-none-eabi-nano\",\n      \"generator\": \"Unix Makefiles\",\n      \"hidden\": true,\n      \"inherits\": [\n        \"ci-arm-none-eabi\"\n      ],\n      \"cacheVariables\": {\n        \"CMAKE_EXE_LINKER_FLAGS\": \"-mcpu=cortex-m7 -mthumb -specs=nano.specs -Wl,--gc-sections\"\n      }\n    },\n    {\n      \"name\": \"coverage-unix\",\n      \"inherits\": [\n        \"ci-build\",\n        \"ci-unix\"\n      ],\n      \"hidden\": true,\n      \"cacheVariables\": {\n        \"ENABLE_COVERAGE\": \"ON\",\n        \"CMAKE_BUILD_TYPE\": \"Coverage\",\n        \"CMAKE_CXX_FLAGS_COVERAGE\": \"-O0 -g --coverage\",\n        \"CMAKE_EXE_LINKER_FLAGS_COVERAGE\": \"--coverage\",\n        \"CMAKE_SHARED_LINKER_FLAGS_COVERAGE\": \"--coverage\"\n      }\n    },\n    {\n      \"name\": \"ci-coverage\",\n      \"inherits\": [\n        \"coverage-unix\",\n        \"dev-mode\"\n      ],\n      \"cacheVariables\": {\n        \"COVERAGE_HTML_COMMAND\": \"\"\n      }\n    },\n    {\n      \"name\": \"ci-sanitize\",\n      \"inherits\": [\n        \"ci-build\",\n        \"ci-unix\",\n        \"dev-mode\"\n      ],\n      \"cacheVariables\": {\n        \"CMAKE_BUILD_TYPE\": \"Sanitize\",\n        \"CMAKE_CXX_FLAGS_SANITIZE\": \"-O2 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common\"\n      }\n    },\n    {\n      \"name\": \"ci-build\",\n      \"binaryDir\": \"${sourceDir}/build\",\n      \"hidden\": true\n    },\n    {\n      \"name\": \"ci-ubuntu\",\n      \"inherits\": [\n        \"ci-build\",\n        \"ci-unix\",\n        \"dev-mode\"\n      ]\n    },\n    {\n      \"name\": \"ci-memcheck\",\n      \"inherits\": [\n        \"ci-build\",\n        \"ci-unix\",\n        \"valgrind\",\n        \"dev-mode\"\n      ]\n    },\n    {\n      \"name\": \"ci-checks\",\n      \"inherits\": [\n        \"ci-build\",\n        \"ci-unix\",\n        \"clang-tidy\",\n        \"cppcheck\",\n        \"dev-mode\"\n      ]\n    },\n    {\n      \"name\": \"ci-embedded\",\n      \"inherits\": [\n        \"ci-build\",\n        \"ci-arm-none-eabi\",\n        \"dev-mode\"\n      ]\n    },\n    {\n      \"name\": \"ci-size-coverage\",\n      \"inherits\": [\n        \"ci-build\",\n        \"ci-unix\",\n        \"dev-mode\"\n      ],\n      \"cacheVariables\": {\n        \"CMAKE_CXX_FLAGS\": \"$env{CXX_FLAGS} -ffunction-sections -fdata-sections -g0 -fno-exceptions -fno-rtti -fno-asynchronous-unwind-tables\",\n        \"BUILD_SIZE_COVERAGE\": \"ON\",\n        \"ENABLE_SIZE_COVERAGE\": \"ON\",\n        \"SIZE_COVERAGE_HTML_COMMAND\": \"\",\n        \"SIZE_TOOL\": \"size\"\n      }\n    },\n    {\n      \"name\": \"ci-size-coverage-embedded\",\n      \"inherits\": [\n        \"ci-build\",\n        \"ci-arm-none-eabi\",\n        \"dev-mode\"\n      ],\n      \"cacheVariables\": {\n        \"BUILD_SIZE_COVERAGE\": \"ON\",\n        \"ENABLE_SIZE_COVERAGE\": \"ON\",\n        \"SIZE_COVERAGE_HTML_COMMAND\": \"\",\n        \"SIZE_TOOL\": \"arm-none-eabi-size\"\n      }\n    },\n    {\n      \"name\": \"ci-size-coverage-embedded-nano\",\n      \"inherits\": [\n        \"ci-build\",\n        \"ci-arm-none-eabi-nano\",\n        \"dev-mode\"\n      ],\n      \"cacheVariables\": {\n        \"BUILD_SIZE_COVERAGE\": \"ON\",\n        \"ENABLE_SIZE_COVERAGE\": \"ON\",\n        \"SIZE_COVERAGE_HTML_COMMAND\": \"\",\n        \"SIZE_TOOL\": \"arm-none-eabi-size\"\n      }\n    }\n  ],\n  \"buildPresets\": [\n    {\n      \"name\": \"default\",\n      \"configurePreset\": \"ci-ubuntu\"\n    }\n  ]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2021 - present, Toni Neubert (viatorus/emio)\n\nCopyright (c) 2012 - present, Victor Zverovich (fmtlib/fmt)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![logo](docs/res/logo.png)\n\n[![Continuous Integration](https://github.com/Viatorus/emio/actions/workflows/ci.yml/badge.svg)](https://github.com/Viatorus/emio/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/Viatorus/emio/branch/main/graph/badge.svg?token=7BQFK1PNLX)](https://codecov.io/gh/Viatorus/emio)\n[![Conan Center](https://img.shields.io/conan/v/emio)](https://conan.io/center/recipes/emio)\n\n**em{io}** is a safe and fast high-level and low-level character input/output library for bare-metal and RTOS based\nembedded systems with a very small binary footprint.\n\n```cpp\n// High-level\nstd::string str = emio::format(\"The answer is {}.\", 42);  // Format argument.\n\nint answer{};\nemio::result<void> scan_res = emio::scan(str, \"The answer is {}.\", answer);  // Scan input string.\nif (scan_res) {\n    emio::print(\"The answer is {}.\", answer);  // Output to console.\n}\n\n// Without using heap.\nemio::static_buffer<128> buf{}; \nemio::format_to(buf, \"The answer is {:#x}.\", 42).value();\nbuf.view();  // <- The answer is 0x2a.\n\n// Low-level\nemio::writer wrt{buf};\nwrt.write_str(\" In decimal: \").value();\nwrt.write_int(42).value();\nwrt.write_char('.').value();\nbuf.view();  // <- The answer is 0x2a. In decimal: 42.\n\nemio::reader rdr{\"17c\"};\nEMIO_TRY(uint32_t number, rdr.parse_int<uint32_t>());  // <- 17\nEMIO_TRY(char suffix, rdr.read_char());                // <- c\n```\n\n[**This library is in beta status! Please help to make it fly!**](https://github.com/Viatorus/emio/milestone/1)\n\n* [API documentation](docs/API.md)\n* Try emio [online](https://godbolt.org/z/fP7z7MzbG).\n\n## Yet another character input/output library  \n\nBare-metal and RTOS based embedded systems do have special requirements which are mostly overlooked by the C++ standard,\nits implementations and other libraries.\n\nTherefore, this library:\n\n* has a very small binary footprint **(~38 times smaller than fmtlib!)**\n* returns a result object instead of throwing an exception\n* provides a high-level and low-level API which can be used at compile-time\n\nRead more about it in the [DESIGN](docs/DESIGN.md) document.\n\n## Including emio in your project\n\n- With CMake and fetch content\n\n```cmake\nFetchContent_Declare(\n        emio\n        GIT_TAG main\n        GIT_REPOSITORY https://github.com/Viatorus/emio.git\n        GIT_SHALLOW TRUE\n)\nFetchContent_MakeAvailable(emio)\n```\n\n- Download the [single header file](https://viatorus.github.io/emio/) generated with [Quom](https://github.com/Viatorus/quom)\n- From [Conan Center](https://conan.io/center/recipes/emio)\n\nA compiler supporting C++20 is required. Tested with GCC 11/12/13 and Clang 16/17.\n\n## Contributing\n\nSee the [CONTRIBUTING](docs/CONTRIBUTING.md) document.\n\n## Licensing\n\nem{io} is distributed under the [MIT license](LICENSE).\n"
  },
  {
    "path": "cmake/arm-none-eabi-toolchain.cmake",
    "content": "cmake_minimum_required(VERSION 3.14.0)\n\nset(CMAKE_SYSTEM_NAME Linux)\nset(CMAKE_SYSTEM_VERSION 1)\nset(CMAKE_SYSTEM_PROCESSOR arm)\n\nset(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # do not link executable when testing the compiler.\n\nset(TOOLCHAIN_BIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi/bin)\n\nset(CMAKE_C_COMPILER ${TOOLCHAIN_BIN_PATH}/arm-none-eabi-gcc)\nset(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN_PATH}/arm-none-eabi-g++)\nset(CMAKE_AR ${TOOLCHAIN_BIN_PATH}/arm-none-eabi-gcc-ar)\nset(CMAKE_RANLIB ${TOOLCHAIN_BIN_PATH}/arm-none-eabi-gcc-ranlib)\n"
  },
  {
    "path": "cmake/coverage.cmake",
    "content": "# ---- Variables ----\n\n# We use variables separate from what CTest uses, because those have\n# customization issues\nset(\n        COVERAGE_TRACE_COMMAND\n        lcov -c -q\n        -o \"${PROJECT_BINARY_DIR}/coverage.info\"\n        -d \"${PROJECT_BINARY_DIR}\"\n        --include \"${PROJECT_SOURCE_DIR}/*\"\n        CACHE STRING\n        \"; separated command to generate a trace for the 'coverage' target\"\n)\n\nset(\n        COVERAGE_HTML_COMMAND\n        genhtml --legend -f -q\n        \"${PROJECT_BINARY_DIR}/coverage.info\"\n        -p \"${PROJECT_SOURCE_DIR}\"\n        -o \"${PROJECT_BINARY_DIR}/coverage_html\"\n        CACHE STRING\n        \"; separated command to generate an HTML report for the 'coverage' target\"\n)\n\n# ---- Coverage target ----\n\nadd_custom_target(\n        coverage\n        COMMAND ${COVERAGE_TRACE_COMMAND}\n        COMMAND ${COVERAGE_HTML_COMMAND}\n        COMMENT \"Generating coverage report\"\n        VERBATIM\n)\n"
  },
  {
    "path": "cmake/dev-mode.cmake",
    "content": "include(cmake/folders.cmake)\n\nset_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1)\ninclude(CTest)\n\nif (BUILD_TESTING OR BUILD_SIZE_COVERAGE)\n    set(CMAKE_MODULE_PATH\n            CACHE INTERNAL\n            FORCE\n            )\n\n    set(FMT_HEADERS\n            CACHE INTERNAL\n            FORCE\n            )\n\n    set(FMT_OS OFF\n            CACHE INTERNAL\n            \"Path to downloaded Catch2 modules\"\n            FORCE\n            )\n\n\n    Include(FetchContent)\n    FetchContent_Declare(\n            fmt\n            GIT_TAG d9bc5f1320332db9a4bf7e103b0813b94e369304  # 9.1.1 - not released\n            GIT_REPOSITORY https://github.com/fmtlib/fmt.git\n    )\n    FetchContent_MakeAvailable(fmt)\nendif ()\n\nif (BUILD_TESTING)\n    Include(FetchContent)\n    FetchContent_Declare(\n            Catch2\n            GIT_TAG v3.4.0\n            GIT_REPOSITORY https://github.com/catchorg/Catch2.git\n            GIT_SHALLOW TRUE\n    )\n    FetchContent_MakeAvailable(Catch2)\nendif ()\n\n\nif (BUILD_TESTING)\n    add_subdirectory(test/benchmark)\n    add_subdirectory(test/compile_test)\n    add_subdirectory(test/static_analysis)\n    add_subdirectory(test/unit_test)\nendif ()\n\nif (BUILD_SIZE_COVERAGE)\n    add_subdirectory(test/size_test)\nendif ()\n\noption(ENABLE_COVERAGE \"Enable coverage support separate from CTest's\" OFF)\nif (ENABLE_COVERAGE)\n    include(cmake/coverage.cmake)\nendif ()\n\ninclude(cmake/lint-targets.cmake)\n\nadd_folders(Project)\n"
  },
  {
    "path": "cmake/elf_to_size_coverage.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport re\nimport subprocess\nimport sys\nfrom io import StringIO\nfrom pathlib import Path\n\n\ndef parse_args(args):\n    parser = argparse.ArgumentParser(\n        description='Creates the size difference of a base file size to multiple other files sizes as lcov info file')\n    parser.add_argument('--size-tool', type=Path, help='path to the GNU size tool', metavar='SIZE_TOOL', required=True)\n    parser.add_argument('base', type=Path, help='the first file is used as base size', metavar='BASE_FILE')\n    parser.add_argument('files', type=Path, nargs='+', help='file(s) to compare', metavar='FILE')\n    parser.add_argument('--output', '-o', type=Path, help='specify the output file', metavar='OUTPUT_FILE',\n                        required=True)\n\n    return parser.parse_args(args)\n\n\nclass Generator:\n    def __init__(self, size_tool, base_file, out):\n        self._size_tool = size_tool\n        self._base_file = base_file.absolute()\n        self._base_size = self._get_size(base_file)\n        self._out = out\n        self._out.write(\"TN:\\n\")\n\n        print(\"{}: {} (base)\".format(base_file, self._base_size))\n\n    def generate(self, file: Path):\n        file_path = file.absolute()\n        file_size = self._get_size(file)\n\n        print('{} - {}'.format(file_path, file_size))\n\n        if file_size < self._base_size:\n            raise Exception(f'{file} cannot be smaller than {self._base_file}')\n\n        self._out.write(f'SF:{file_path}\\n')\n        for covered in range(self._base_size):\n            self._out.write(f'DA:{covered},1\\n')\n\n        for uncovered in range(self._base_size, file_size):\n            self._out.write(f'DA:{uncovered},0\\n')\n        self._out.write('end_of_record\\n')\n\n    def _get_size(self, file: Path):\n        output = subprocess.check_output([str(self._size_tool), '--format=GNU', str(file)], encoding='UTF-8')\n        # text       data        bss      total filename\n        #  348        120         28        496  base\n        # Get total from regex:\n        total = re.search('text\\s+data\\s+bss\\s+total\\s+filename\\n\\s+\\d+\\s+\\d+\\s+\\d+\\s+(\\d+)\\s+', output)\n        if not total:\n            raise Exception(f'Unexpected output from size: {output}')\n        return int(total[1])\n\n\ndef run():\n    args = parse_args(sys.argv[1:])\n\n    if not args.base.is_file():\n        raise Exception(f'Base file not found: {args.base}')\n\n    output = StringIO()\n    generator = Generator(args.size_tool, args.base, output)\n\n    for file in args.files:\n        if not file.is_file():\n            raise Exception(f'File not found: {file}')\n        generator.generate(file)\n\n    args.output.write_text(output.getvalue())\n\n\nif __name__ == '__main__':\n    run()\n"
  },
  {
    "path": "cmake/folders.cmake",
    "content": "set_property(GLOBAL PROPERTY USE_FOLDERS YES)\n\n# Call this function at the end of a directory scope to assign a folder to\n# targets created in that directory. Utility targets will be assigned to the\n# UtilityTargets folder, otherwise to the ${name}Targets folder. If a target\n# already has a folder assigned, then that target will be skipped.\nfunction(add_folders name)\n    get_property(targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)\n    foreach (target IN LISTS targets)\n        get_property(folder TARGET \"${target}\" PROPERTY FOLDER)\n        if (DEFINED folder)\n            continue()\n        endif ()\n        set(folder Utility)\n        get_property(type TARGET \"${target}\" PROPERTY TYPE)\n        if (NOT type STREQUAL \"UTILITY\")\n            set(folder \"${name}\")\n        endif ()\n        set_property(TARGET \"${target}\" PROPERTY FOLDER \"${folder}Targets\")\n    endforeach ()\nendfunction()\n"
  },
  {
    "path": "cmake/install-config.cmake",
    "content": "include(\"${CMAKE_CURRENT_LIST_DIR}/emioTargets.cmake\")\n"
  },
  {
    "path": "cmake/install-rules.cmake",
    "content": "if (PROJECT_IS_TOP_LEVEL)\n    set(CMAKE_INSTALL_INCLUDEDIR include/emio CACHE PATH \"\")\nendif ()\n\n# Project is configured with no languages, so tell GNUInstallDirs the lib dir\nset(CMAKE_INSTALL_LIBDIR lib CACHE PATH \"\")\n\ninclude(CMakePackageConfigHelpers)\ninclude(GNUInstallDirs)\n\n# find_package(<package>) call for consumers to find this project\nset(package emio)\n\ninstall(\n        DIRECTORY include/\n        DESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}\"\n        COMPONENT emio_Development\n)\n\ninstall(\n        TARGETS emio_emio\n        EXPORT emioTargets\n        INCLUDES DESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}\"\n)\n\nwrite_basic_package_version_file(\n        \"${package}ConfigVersion.cmake\"\n        COMPATIBILITY SameMajorVersion\n        ARCH_INDEPENDENT\n)\n\n# Allow package maintainers to freely override the path for the configs\nset(\n        emio_INSTALL_CMAKEDIR \"${CMAKE_INSTALL_DATADIR}/${package}\"\n        CACHE PATH \"CMake package config location relative to the install prefix\"\n)\nmark_as_advanced(emio_INSTALL_CMAKEDIR)\n\ninstall(\n        FILES cmake/install-config.cmake\n        DESTINATION \"${emio_INSTALL_CMAKEDIR}\"\n        RENAME \"${package}Config.cmake\"\n        COMPONENT emio_Development\n)\n\ninstall(\n        FILES \"${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake\"\n        DESTINATION \"${emio_INSTALL_CMAKEDIR}\"\n        COMPONENT emio_Development\n)\n\ninstall(\n        EXPORT emioTargets\n        NAMESPACE emio::\n        DESTINATION \"${emio_INSTALL_CMAKEDIR}\"\n        COMPONENT emio_Development\n)\n\nif (PROJECT_IS_TOP_LEVEL)\n    include(CPack)\nendif ()\n"
  },
  {
    "path": "cmake/lint-targets.cmake",
    "content": "set(\n        FORMAT_PATTERNS\n        source/*.cpp source/*.hpp\n        include/*.hpp\n        test/*.cpp test/*.hpp\n        example/*.cpp example/*.hpp\n        CACHE STRING\n        \"; separated patterns relative to the project source dir to format\"\n)\n\nset(FORMAT_COMMAND clang-format CACHE STRING \"Formatter to use\")\n\nadd_custom_target(\n        format-check\n        COMMAND \"${CMAKE_COMMAND}\"\n        -D \"FORMAT_COMMAND=${FORMAT_COMMAND}\"\n        -D \"PATTERNS=${FORMAT_PATTERNS}\"\n        -P \"${PROJECT_SOURCE_DIR}/cmake/lint.cmake\"\n        WORKING_DIRECTORY \"${PROJECT_SOURCE_DIR}\"\n        COMMENT \"Linting the code\"\n        VERBATIM\n)\n\nadd_custom_target(\n        format-fix\n        COMMAND \"${CMAKE_COMMAND}\"\n        -D \"FORMAT_COMMAND=${FORMAT_COMMAND}\"\n        -D \"PATTERNS=${FORMAT_PATTERNS}\"\n        -D FIX=YES\n        -P \"${PROJECT_SOURCE_DIR}/cmake/lint.cmake\"\n        WORKING_DIRECTORY \"${PROJECT_SOURCE_DIR}\"\n        COMMENT \"Fixing the code\"\n        VERBATIM\n)\n"
  },
  {
    "path": "cmake/lint.cmake",
    "content": "cmake_minimum_required(VERSION 3.14)\n\nmacro(default name)\n    if (NOT DEFINED \"${name}\")\n        set(\"${name}\" \"${ARGN}\")\n    endif ()\nendmacro()\n\ndefault(FORMAT_COMMAND clang-format)\ndefault(\n        PATTERNS\n        source/*.cpp source/*.hpp\n        include/*.hpp\n        test/*.cpp test/*.hpp\n        example/*.cpp example/*.hpp\n)\ndefault(FIX NO)\n\nset(flag --output-replacements-xml)\nset(args OUTPUT_VARIABLE output)\nif (FIX)\n    set(flag -i)\n    set(args \"\")\nendif ()\n\nfile(GLOB_RECURSE files ${PATTERNS})\nset(badly_formatted \"\")\nset(output \"\")\nstring(LENGTH \"${CMAKE_SOURCE_DIR}/\" path_prefix_length)\n\nforeach (file IN LISTS files)\n    execute_process(\n            COMMAND \"${FORMAT_COMMAND}\" --style=file \"${flag}\" \"${file}\"\n            WORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\n            RESULT_VARIABLE result\n            ${args}\n    )\n    if (NOT result EQUAL \"0\")\n        message(FATAL_ERROR \"'${file}': formatter returned with ${result}\")\n    endif ()\n    if (NOT FIX AND output MATCHES \"\\n<replacement offset\")\n        string(SUBSTRING \"${file}\" \"${path_prefix_length}\" -1 relative_file)\n        list(APPEND badly_formatted \"${relative_file}\")\n    endif ()\n    set(output \"\")\nendforeach ()\n\nif (NOT badly_formatted STREQUAL \"\")\n    list(JOIN badly_formatted \"\\n\" bad_list)\n    message(\"The following files are badly formatted:\\n\\n${bad_list}\\n\")\n    message(FATAL_ERROR \"Run again with FIX=YES to fix these files.\")\nendif ()\n"
  },
  {
    "path": "cmake/prelude.cmake",
    "content": "# ---- In-source guard ----\n\nif (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)\n    message(\n            FATAL_ERROR\n            \"In-source builds are not supported. \"\n            \"Please read the BUILDING document before trying to build this project. \"\n            \"You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first.\"\n    )\nendif ()\n"
  },
  {
    "path": "cmake/project-is-top-level.cmake",
    "content": "# This variable is set by project() in CMake 3.21+\nstring(\n        COMPARE EQUAL\n        \"${CMAKE_SOURCE_DIR}\" \"${PROJECT_SOURCE_DIR}\"\n        PROJECT_IS_TOP_LEVEL\n)\n"
  },
  {
    "path": "cmake/size-coverage.cmake",
    "content": "# ---- Variables ----cd\n\nget_filename_component(COMPILER_DIR ${CMAKE_CXX_COMPILER} DIRECTORY)\n\n# We use variables separate from what CTest uses, because those have\n# customization issues\nset(\n        SIZE_COVERAGE_TRACE_COMMAND\n        python3 ${CMAKE_SOURCE_DIR}/cmake/elf_to_size_coverage.py\n        --size-tool \"${COMPILER_DIR}/${SIZE_TOOL}\"\n        -o \"${PROJECT_BINARY_DIR}/size-coverage.info\"\n        ${SIZE_COVERAGE_FILES}\n        CACHE STRING\n        \"; separated command to generate a trace for the 'size-coverage' target\"\n)\n\nset(\n        SIZE_COVERAGE_HTML_COMMAND\n        genhtml --legend -f -q\n        \"${PROJECT_BINARY_DIR}/size-coverage.info\"\n        -p \"${PROJECT_SOURCE_DIR}\"\n        -o \"${PROJECT_BINARY_DIR}/size_coverage_html\"\n        CACHE STRING\n        \"; separated command to generate an HTML report for the 'size-coverage' target\"\n)\n\n# ---- Coverage target ----\n\nadd_custom_target(\n        size-coverage\n        COMMAND ${SIZE_COVERAGE_TRACE_COMMAND}\n        COMMAND ${SIZE_COVERAGE_HTML_COMMAND}\n        COMMENT \"Generating size-coverage report\"\n        VERBATIM\n)\n"
  },
  {
    "path": "cmake/variables.cmake",
    "content": "# ---- Developer mode ----\n\n# Developer mode enables targets and code paths in the CMake scripts that are\n# only relevant for the developer(s) of emio\n# Targets necessary to build the project must be provided unconditionally, so\n# consumers can trivially build and package the project\nif (PROJECT_IS_TOP_LEVEL)\n    option(emio_DEVELOPER_MODE \"Enable developer mode\" OFF)\nendif ()\n\n# ---- Warning guard ----\n\n# target_include_directories with the SYSTEM modifier will request the compiler\n# to omit warnings from the provided paths, if the compiler supports that\n# This is to provide a user experience similar to find_package when\n# add_subdirectory or FetchContent is used to consume this project\nset(warning_guard \"\")\nif (NOT PROJECT_IS_TOP_LEVEL)\n    option(\n            emio_INCLUDES_WITH_SYSTEM\n            \"Use SYSTEM modifier for emio's includes, disabling warnings\"\n            ON\n    )\n    mark_as_advanced(emio_INCLUDES_WITH_SYSTEM)\n    if (emio_INCLUDES_WITH_SYSTEM)\n        set(warning_guard SYSTEM)\n    endif ()\nendif ()\n\n# Export compile commands for external tools.\nset(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n"
  },
  {
    "path": "docs/API.md",
    "content": "# API\n\nThis is a small API overview. The public API is fully documented inside the source code. Unless otherwise stated,\neverything can be used at compile-time.\n\nThe public namespace is `emio` only - no deeper nesting.\n\n* [err](#err)\n* [result](#result)\n* [Buffer](#buffer)\n    + [memory_buffer](#memorybuffer)\n    + [span_buffer](#spanbuffer)\n    + [static_buffer](#staticbuffer)\n    + [iterator_buffer](#iteratorbuffer)\n    + [file_buffer](#filebuffer)\n    + [truncating_buffer](#truncatingbuffer)\n* [Reader](#reader)\n* [Writer](#writer)\n* [Format](#format)\n    + [Dynamic format specification](#dynamic-format-specification)\n    + [Formatter](#formatter)\n* [Print](#print)\n* [Scan](#scan)\n    + [Scanner](#scanner)\n\n## err\n\nA list of possible I/O errors as enum.\nEvery function describes the possible errors which can occur. See the source code documentation for more information.\n\n`eof`\n\n- End of file (e.g. reaching the end of an output array).\n\n`invalid_argument`\n\n- A parameter is incorrect (e.g. the output base is invalid).\n\n`invalid_data`\n\n- The data is malformed (e.g. no digit where a digit was expected).\n\n`out_of_range`\n\n- The parsed value is not in the range representable by the type (e.g. parsing 578 as uint8_t).\n\n`invalid_format`\n\n- The format string is invalid (e.g. missing arguments, wrong syntax).\n\n`to_string(err) -> string_view`\n\n- Returns the name of the error code.\n\n*Example*\n\n```cpp\nstd::string_view error_msg = emio::to_string(emio::err::invalid_format);  // invalid_format\n```\n\n## result\n\n`template <typename T> class result;`\n\n- The return type of almost all functions to propagate a value of type `T` on success or an error of type `emio::err`\n  on failure.\n\n*constructor(arg)*\n\n- Constructable either from T or `emio::err`.\n\n`has_value() -> bool`\n\n- Checks whether the object holds a value.\n\n`has_error() -> bool`\n\n- Checks whether the object holds an error.\n\n`value() -> T`\n\n- Returns the value or throws/terminates if no value is held.\n\n`value_or(T) -> T`\n\n- Returns the value or returns the passed alternative if no value is held.\n\n`assume_value() -> T`\n\n- Returns the value without any checks. Invokes undefined behavior if no value is held.\n\n`error() -> emio::err`\n\n- Returns the error or throws/terminates if no error is held.\n\n`assume_error() -> emio::err`\n\n- Returns the error without any checks. Invokes undefined behavior if no error is held.\n\nThere exists two helper macros to simplify the control flow:\n\n`EMIO_TRYV(expr)`\n\n- Evaluates an expression *expr*. If successful, continues the execution. If unsuccessful, immediately returns from the\n  calling function.\n\n`EMIO_TRY(var, expr)`\n\n- Evaluates an expression *expr*. If successful, assigns the value to a declaration *var*. If unsuccessful, immediately\n  returns from the calling function.\n\n*Example*\n\n```cpp\nresult<int> parse_one(std::string_view sv) {\n    if (sv.empty()) {\n        return emio::err::eof;\n    }\n    if (sv == \"1\") {\n        return 1;\n    } else {\n        return emio::err::invalid_data;\n    }\n}\n\nemio::result<void> parse(std::string_view sv) {\n    EMIO_TRY(int val, parse_one(sv));\n    \n    emio::result<int> res = parse_one(sv);\n    if (!res) {\n        return res.error();\n    }\n    if (res.assume_value() == val) {\n        return emio::success;\n    }\n    return emio::err::invalid_format;\n}\n```\n\n## Buffer\n\nAn abstract class which provides functionality for receiving a contiguous memory region of chars to write into.\n\nThere exist multiple implementation of a buffer, all fulfilling a different use case.\nSome buffers have an internal cache to provide a contiguous memory if the actually output object doesn't provide on.\n\nAdditionally, some buffers can be reset to reuse the total capacity of the storage for the next operation.\nThis invalidates any obtaining view!\n\n### memory_buffer\n\n- An endless growing buffer with an internal storage for small buffer optimization.\n\n*Example*\n\n```cpp\nemio::memory_buffer buf;\n\nemio::result<std::span<char>> area = buf.get_write_area_of(50);\nassert(area);\nstd::string_view view = buf.view();\nassert(buf.capacity() >= view.size());\nstd::string str = buf.str();\nbuf.reset();\n```\n\n### span_buffer\n\n- A buffer over a specific contiguous range.\n\n*Example*\n\n```cpp\nstd::array<char, 512> storage;\nemio::span_buffer buf{storage};\nassert(buf.capacity() == 512);\n\nemio::result<std::span<char>> area = buf.get_write_area_of(50);\nassert(area);\nstd::string_view view = buf.view();\nstd::string str = buf.str();\nbuf.reset();\n```\n\n### static_buffer\n\n- A buffer containing a fixed-size storage.\n\n*Example*\n\n```cpp\nemio::static_buffer<512> buf{storage}; \nassert(buf.capacity() == 512);\n\nemio::result<std::span<char>> area = buf.get_write_area_of(50);\nassert(area);\nstd::string_view view = buf.view();\nstd::string str = buf.str();\nbuf.reset();\n```\n\n### iterator_buffer\n\n- A buffer for all kinds of output iterators (raw-pointers, back_insert_iterator or any other output iterator).\n- The buffer's with a direct output iterator (e.g. std::string::iterator) do have an internal cache.\n\n*Example*\n\n```cpp\nstd::string storage{\"filled with something up\"};\nemio::iterator_buffer buf{std::back_inserter(storage)}; \n\nemio::result<std::span<char>> area = buf.get_write_area_of(50);\nassert(area);\nassert(buf.flush());\nstd::back_insert_iterator<std::string> out = buf.out();\n```\n\n### file_buffer\n\n- A buffer over an std::File (file stream) with an internal cache.\n\n*Example*\n\n```cpp\nstd::FILE* file = std::fopen(\"test\", \"w\");\nemio::file_buffer buf{file}; \n\nemio::result<std::span<char>> area = buf.get_write_area_of(50);\nassert(area);\nassert(buf.flush());\nbuf.reset();\n```\n\n### truncating_buffer\n\n- A buffer which truncates the remaining output if the limit of another provided buffer is reached.\n\n*Example*\n\n```cpp\nemio::static_buffer<48> primary_buf{};\nemio::truncating_buffer buf{primary_buf, 32};\n\nemio::result<std::span<char>> area = buf.get_write_area_of(50);\nassert(area);\nassert(buf.flush());\nassert(primary_buf.view().size() == 32);  // Only 32 bytes are flushed.\n```\n\n## Reader\n\n` class reader;`\n\n- A class to read and parse a char sequence like a finite input stream.\n\n*constructor(input)*\n\n- Constructable from any suitable char sequence.\n\n*Example*\n\n```cpp\nemio::reader input{\"1\"};\nassert(input.cnt_remaining() == 1);\nassert(input.view_remaining() == \"1\");\n\nstd::string foo{\"foo\"};\nemio::reader input2{foo};\nassert(input2.cnt_remaining() == 3);\nassert(input2.view_remaining() == \"foo\");\n```\n\n`peek() -> result<char>`\n\n- Returns the next char without consuming it.\n\n*Example*\n\n```cpp\nemio::reader input{\"abc\"};\nemio::result<char> res = input.peek();\nassert(res == 'a');\n```\n\n`read_char() -> result<char>`\n\n- Returns one char.\n\n*Example*\n\n```cpp\nemio::reader input{\"abc\"};\nemio::result<char> res = input.read_char();\nassert(res == 'a');\n```\n\n`read_n_char(n) -> result<string_view>`\n\n- Returns *n* chars.\n\n*Example*\n\n```cpp\nemio::reader input{\"abc\"};\nemio::result<std::string_view> res = input.read_n_char(3);\nassert(res == \"abc\");\n```\n\n`parse_int<T>(base = 10) -> result<T>`\n\n- Parses an integer of type *T* with a specific *base*.\n\n*Example*\n\n```cpp\nemio::reader input{\"abc\"};\nemio::result<int> res = input.read_int(16 /* hexadecimal */);\nassert(res == 0xabc);\n```\n\n`read_until/_char/str/any_of/none_of/([predicate,] options) -> result<string_view>`\n\n- Reads n chars until a given *predicate* (delimiter/group/function) applies.\n- Has *options* to configure what should happen with the predicate and what should happen if EOF is reached.\n\n*Example*\n\n```cpp\nemio::reader get_input() {\n    return emio::reader{\"abc\"};\n}\n\n// read_until_char\nemio::result<std::string_view> res = get_input().read_until_char('c');\nassert(res == \"ab\");\n\n// read_until_str\nemio::result<std::string_view> res = get_input().read_until_str(\"bc\");\nassert(res == \"a\");\n\n// read_until_any_of\nemio::result<std::string_view> res = get_input().read_until_any_of(\"cd\");\nassert(res == \"ab\");\n\n// read_until_none_of\nemio::result<std::string_view> res = get_input().read_until_none_of(\"ab\");\nassert(res == \"ab\");\n\n// read_until with predicate\nemio::result<std::string_view> res = get_input().read_until([](char c) { return c != 'a';});\nassert(res == \"a\");\n```\n\n```cpp\n// Different options.\nemio::reader input{\"abc 123 Hello\"};\n\n// with include_delimiter option \nemio::result<std::string_view> res = input.read_until_str(\" \", {.include_delimiter = true});\nassert(res == \"abc \");\n\n// with keep keep_delimiter option \nemio::result<std::string_view> res = input.read_until_str(\"Hello\", {.keep_delimiter = true});\nassert(res == \"123 \");\n\n//  with ignore_eof option \nemio::result<std::string_view> res = input.read_until_str(\"xyz\", {.ignore_eof = true});\nassert(res == \"Hello\");\n```\n\n`read_if_match_char/str(c/str) -> result<char/std::string_view>`\n\n- Reads one/multiple chars if *c/str* matches the next char/chars.\n\n*Example*\n\n```cpp\nemio::reader input{\"abc\"};\nif (input.read_if_match_char('a')) {\n  emio::result<std::string_view> res = input.read_if_match_str(\"bc\");   \n  assert(res == \"bc\");   \n}\n```\n\n## Writer\n\n`class writer;`\n\n- A class to write sequences of characters or other kinds of data into an output buffer.\n\n*constructor(buffer)*\n\n*Example*\n\n```cpp\nemio::static_buffer<128> buf;\nemio::writer output{buf};\n```\n\n- Constructable from a reference to a buffer.\n\n`write_char(c) -> result<void>`\n\n- Writes a char *c* into the buffer.\n\n*Example*\n\n```cpp\nemio::writer output{get_buffer()};\nemio::result<void> res = output.write_char('a');  // Buffer contains \"a\"\nassert(res);\n```\n\n`write_char_n(c, n) -> result<void>`\n\n- Writes a char *c* *n* times into the buffer.\n\n*Example*\n\n```cpp\nemio::writer output{get_buffer()};\nemio::result<void> res = output.write_char_n('a', 5);  // Buffer contains \"aaaaa\"\nassert(res);\n```\n\n`write_char_escaped(c) -> result<void>`\n\n- Writes a char *c* escaped into the buffer.\n\n*Example*\n\n```cpp\nemio::writer output{get_buffer()};\nemio::result<void> res = output.write_char_escaped('\\n', 5);  // Buffer contains \"\\\\n\"\nassert(res);\n```\n\n`write_str(sv) -> result<void>`\n\n- Writes a char sequence *sv* into the buffer.\n\n*Example*\n\n```cpp\nemio::writer output{get_buffer()};\nemio::result<void> res = output.write_str(\"Hello\");  // Buffer contains \"Hello\"\nassert(res);\n```\n\n`write_str_escaped(sv) -> result<void>`\n\n- Writes a char sequence *sv* escaped into the buffer.\n\n*Example*\n\n```cpp\nemio::writer output{get_buffer()};\nemio::result<void> res = output.write_str(\"\\t 'and'\");  // Buffer contains \"\\\\t \\'and\\'\"\nassert(res);\n```\n\n`write_int(integer, options) -> result<void>`\n\n- Writes an *integer* into the buffer.\n- Has *options* to configure the base and if the alphanumerics should be in lower or upper case.\n\n*Example*\n\n```cpp\nemio::writer output{get_buffer()};\nemio::result<void> res = output.write_int(15);  // Buffer contains \"15\"\nassert(res);\n\nres = output.write_int(15, {.base = 16, upper_case = true});  // Buffer contains \"15F\"\nassert(res);\n```\n\n## Format\n\nThe following functions use a format string syntax which is nearly identical to the one used in\n[fmt](https://fmt.dev/latest/syntax.html), which is similar to\n[str.format](https://docs.python.org/3/library/stdtypes.html#str.format) in Python.\n\nThings that are missing:\n\n- chrono syntax (planned)\n- 'a'/'A' for hexadecimal floating point format (TBD)\n- UTF-8 support (TBD)\n- using an identifier as arg_id: `fmt::format(\"{nbr}\", fmt::arg(\"nbr\", 42)` (TBD)\n- `'L'` options for locale (somehow possible but not with std::locale because of the binary size)\n\nThe grammar for the replacement field is as follows:\n\n```sass\nreplacement_field ::=  \"{\" [arg_id] [\":\" format_spec] \"}\"\n\narg_id            ::=  integer\n\ninteger           ::=  digit+\n\ndigit             ::=  \"0\"...\"9\"\n```\n\nThe grammar for the format specification is as follows:\n\n```sass\nformat_spec ::=  [[fill]align][sign][\"#\"][\"0\"][width][type]\n\nfill        ::=  <a character other than '{' or '}'>\n\nalign       ::=  \"<\" | \">\" | \"^\"\n\nsign        ::=  \"+\" | \"-\" | \" \"\n\nwidth       ::=  integer\n\ntype        ::=  \"b\" | \"B\" | \"c\" | \"d\" | \"o\" | \"s\" | \"x\" | \"X\" | \"e\" | \"E\" | \"f\" | \"F\" | \"g\" | \"G\"\n```\n\nThe syntax of the format string is validated at compile-time. If a validation at runtime is required, the string\nmust be wrapped inside a `runtime_string` object. There is a simple helper function for that:\n\n`runtime(string_view) -> runtime_string`\n\nSome functions (like `format` or `formatted_size`) are further optimized (simplified) in their return type if the format\nstring is a valid-only format string that could be ensured at compile-time.\n\n`format(format_str, ...args) -> string/result<string>`\n\n*Example*\n\n```cpp\nstd::string str = emio::format(\"Hello {}!\", 42);\nassert(str == \"Hello 42!\");\n\nstd::string format_str = \"Good by {}!\";\nemio::result<std::string> res = emio::format(emio::runtime(format_str), 42);\nassert(res == \"Good by 42!\");\n```\n\n- Formats arguments according to the format string, and returns the result as a string.\n- The return value depends on the type of the format string (valid-only type or not).\n\n`format_to(out, format_str, ...args) -> result<Output>`\n\n- Formats arguments according to the format string, and writes the result to the output iterator/buffer.\n  **Note** If a raw output pointer or simple output iterator is used, no range checking can take place!\n\n*Example*\n\n```cpp\nstd::string out;\nout.resize(10);\nemio::result<std::string::iterator> res = emio::format_to(out.begin(), \"Hello {}!\", 42);\nassert(res);\nassert(out == \"Hello 42!\");\n```\n\n`format_to_n(out, n, format_str, ...args) -> result<format_to_n_result<Output>>`\n\n- Formats arguments according to the format string, and writes the result to the output iterator/buffer. At most *n*\n  characters are written.\n\n*Example*\n\n```cpp\nstd::string out;\nout.resize(10);\nemio::result<emio::format_to_n_result<std::string::iterator>> res = \n        emio::format_to_n(out.begin(), 7, \"Hello {}!\", 42);\nassert(res)\nassert(res->out == \"Hello 4\");\nassert(res->size == 7);\n```\n\n`formatted_size(format_str, ...args) -> size_t/result<size_t>`\n\n- Determines the total number of characters in the formatted string by formatting args according to the format string.\n- The return value depends on the type of the format string (valid-only type or not).\n\n*Example*\n\n```cpp\nsize_t size = emio::formatted_size(\"> {}\", 42);\nassert(size == 4);\n```\n\nFor each function there exists a function prefixed with v (e.g. `vformat`) which takes `format_args` instead of a\nformat string and arguments. The types are erased and can be used in non-template functions to reduce build-time, hide\nimplementations and reduce the binary size. **Note:** These type erased functions cannot be used at compile-time.\n\n`format_args` can be created with:\n\n`make_format_args(format_str, ...args) -> internal format_args_storage`\n\n- Returns an object that stores a format string with an array of all arguments to format.\n- Keep in mind that the storage uses reference semantics and does not extend the lifetime of args. It is the\n  programmer's responsibility to ensure that args outlive the return value.\n\n*Example*\n\n```cpp\nemio::result<void> internal_info(const emio::format_args& args) {\n    emio::memory_buffer buf;\n    \n    emio::writer out{buf};  // Prefix message.\n    EMIO_TRYV(out.write_str(\"INFO: \"));  \n    \n    EMIO_TRYV(emio::vformat_to(out, args));\n    log_message(out.view());  // Forward result. \n    return emio::success;\n}\n\ntemplate<typename...Args>\nvoid log_info(emio::format_string<Args...> fmt, const Args&...args) {\n    emio::result<void> res = internal_info(emio::make_format_args(fmt, args...));  // type-erasing takes place \n    res.value();  // Throw on any error.\n}\n\nvoid do_something(int i) {\n    log_info(\"Do something started with {}.\", i);\n}\n\nint main() {\n    do_something(42);  // INFO: Do something started with 42. \n}\n```\n\n### Dynamic format specification\n\nUnlike other libraries, the format specification cannot be changed through extra replacement fields, as it is possible\ne.g. with fmt to dynamically set the precision to 1 with `fmt::format(\"{:.{}f}\", 3.14, 1);`.\n\nWith emio it is possible to dynamically define _width_ and _precision_ through a `format_spec` object which is then\npassed as an argument with the original value to the format function.\n\n`format_spec{.width = <width>, .precision = <precision>}`\n\n- If a spec is not defined inside the struct, the spec of the parsed format string will be applied.\n\n*Example*\n\n```cpp\nemio::format_spec spec{.precision = 1};\nemio::format(\"{}\", spec.with(3.141592653));  // 3.1\n```\n\n### Formatter\n\nThere exists formatter for builtin types like bool, char, string, integers, floats, void* and non-scoped enums, ranges\nand tuple like types. Support for other standard types (e.g. chrono duration, optional) is planned.\n\nFor formatting values of pointer-like types, simply use `emio::ptr(p)`.\n\n*Example*\n\n```cpp\nint* value = get();\nemio::format(\"{}\", emio::ptr(value));\n```\n\nUse `is_formattable_v<Type>` to check if a type is formattable.\n\nA formatter exists of one optional function `validate` and two mandatory functions `parse` and `format`. If `validate`\nis not present, `parse` must validate the format string.\n\n*Example*\n\n```cpp\nstruct foo {\n    int x;\n};\n\ntemplate <>\nclass emio::formatter<foo> {\n public:\n  /**\n   * Optional static function to validate the format string syntax for this type.\n   * @note If not present, the parse function is invoked for validation.\n   * @param format_rdr The reader over the format string.\n   * @return Success if the format string is valid.\n   */\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  /**\n   * Function to parse the format specs for this type.\n   * @param format_rdr The reader over the format string.\n   * @return Success if the format string is valid and could be parsed.\n   */\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  /**\n   * Function to format the object of this type according to the parsed format specs.\n   * @param out The output writer.\n   * @param arg The argument to format.\n   * @return Success if the formatting could be done.\n   */\n  constexpr result<void> format(writer& out, const foo& arg) const noexcept {\n    return wtr.write_int(arg.x);\n  }\n};\n\nint main() {\n    emio::format(\"{}\", foo{42});  // 42 \n}\n```\n\nIt is also possible to reuse existing formatters via inheritance or composition.\n\n*Example*\n\n```cpp\nstruct foo {\n    int x;\n};\n\ntemplate <>\nclass emio::formatter<foo> : public emio::format<int> {\n public:\n  constexpr result<void> format(writer& out, const foo& arg) noexcept {\n    return emio::format<int>::format(wtr, arg.x);\n  }\n};\n\n\nint main() {\n    emio::format(\"{:#x}\", foo{42});  // 0x2a \n}\n```\n\nIf the `validate` (or if absent the `parse`) function is not constexpr, a runtime format strings must be used. The\n`format` function don't need to be constexpr if the formatting shouldn't be done at compile-time.\n\nFor simple type formatting, like formatting an enum class to its underlying integer or to a string, the function\n`format_as` could be provided. The function must be in the same namespace since ADL is used.\n\n*Example*\n\n```cpp\nnamespace foo {\n    \nenum class bar {\n    foobar,\n    barfoo\n};\n\nconstexpr auto format_as(const bar& w) noexcept {\n  return static_cast<std::underlying_type_t<bar>>(w);\n}\n\n}\n```\n\n## Print\n\nIt is possible to directly print to the standard output or other file streams.\n\n`print(format_str, ...args) -> void/result<void>`\n\n- Formats arguments according to the format string, and writes the result to the standard output stream.\n- The return value depends on the type of the format string (valid-only type or not).\n\n*Example*\n\n```cpp\nemio::print(\"{}!\", 42);  // Outputs: \"42!\"\n\nemio::result<void> res = emio::print(emio::runtime(\"{}!\"), 42);  // Outputs: \"42!\"\nassert(res);\n```\n\n`print(file, format_str, ...args) -> result<void>`\n\n- Formats arguments according to the format string, and writes the result to a file stream.\n\n*Example*\n\n```cpp\nemio::result<void> res = emio::print(stderr, \"{}!\", 42);  // Outputs: \"42!\" to stderr\nassert(res);\n```\n\n`println(format_str, ...args) -> void/result<void>`\n\n- Formats arguments according to the format string, and writes the result to the standard output stream with a new line\n  at the end.\n- The return value depends on the type of the format string (valid-only type or not).\n\n*Example*\n\n```cpp\nemio::println(\"{}!\", 42);  // Outputs: \"42!\" with a line break\n\nemio::result<void> res = emio::println(emio::runtime(\"{}!\"), 42);  // Outputs: \"42!\" with a line break\nassert(res);\n```\n\n`println(file, format_str, ...args) -> result<void>`\n\n- Formats arguments according to the format string, and writes the result to a file stream with a new line at the end.\n\n*Example*\n\n```cpp\nemio::result<void> res = emio::println(stderr, \"{}!\", 42);  // Outputs: \"42!\" with a line break to stderr\nassert(res);\n```\n\nFor each function there exists a function prefixed with v (e.g. `vprint`) which allow the same functionality as\ne.g. `vformat(...)` does for `format(...)`.\n\n## Scan\n\nThe following functions use a format string syntax which is similar to the format syntax of `format`.\n\nThe grammar for the replacement field is the same. The grammar for the scan specific syntax is as follows:\n\n```sass\nformat_spec ::=  [\"#\"][width][type]\n\ntype        ::=  \"b\" | \"B\" | \"c\" | \"d\" | \"o\" | \"s\" | \"x\" | \"X\"\n```\n\n`#`\n\n- for integral types: the alternate form\n    - b/B: `0b` (e.g. 0b10110)\n    - d: nothing (e.g. 9825)\n    - o: leading `0` (e.g. 057)\n    - x/X: `0x` (e.g 0x2fA3)\n- if `#` is present but not the `type`, the base is deduced from the scanned alternate form.\n\n*Example*\n\n```cpp\nint i;\nint j;\nint k;\nint l;\nscan(\"0b101 101 0101 0x101\", \"{:#} {:#} {:#} {:#}\", i, j, k, l);\nassert(i == 0b101);\nassert(j == 101);\nassert(k == 0101);\nassert(l == 0x101);\n```\n\n`width`\n\n- specifies the number of characters to include when parsing an argument\n\n*Example*\n\n```cpp\nint i;\nstd::string_view j;\nint k;\nscan(\"125673\", \"{:2}{:3}{}\", i, j, k);\nassert(i == 12);\nassert(j == \"567\");\nassert(k == 3);\n```\n\n`type`\n\n- for integral types: the base to assume\n    - b/B: base 2 (binary)\n    - d: base 10 (decimal)\n    - o: base 8 (octal)\n    - x/X: base 16 (hexadecimal)\n- c for char\n- s for string/string_view\n\n*Example*\n\n```cpp\nint i;\nint j;\nint k;\nint l;\nscan(\"101 101 101 101\", \"{:b} {:d} {:o} {:x}\", i, j, k, l);\nassert(i == 0b101);\nassert(j == 101);\nassert(k == 0101);\nassert(l == 0x101);\n```\n\nThe syntax of the format string is validated at compile-time. If a validation at runtime is required, the string must\nbe wrapped inside a `runtime_string` object. There is a simple helper function for that:\n\n`runtime(string_view) -> runtime_string`\n\nThe API is structured as follows:\n\n`scan(input, format_str, ...args) -> result<void>`\n\n*Example*\n\n```cpp\nint32_t i;\nuint32_t j;\nemio::result<void> res = emio::scan(\"-1,2\", \"{},{}\", i, j);\nassert(res);\nassert(i == -1);\nassert(j == 2);\n```\n\n- Scans the input string for the given arguments according to the format string.\n\n`scan_from(reader, format_str, ...args) -> result<void>`\n\n- Scans the content of the reader for the given arguments according to the format string.\n\n*Example*\n\n```cpp\nint32_t i;\nuint32_t j;\nemio::reader input{\"-1,2...\"};\nemio::result<void> res = emio::scan_from(input, \"{},{}\", i, j);\nassert(res);\nassert(i == -1);\nassert(j == 2);\nassert(input.view_remaining() == \"...\");\n```\n\nFor each function there exists a function prefixed with v (e.g. `vscan`) which takes `scan_args` instead of a format\nstring and arguments. The types are erased and can be used in non-template functions to reduce build-time, hide\nimplementations and reduce the binary size. **Note:** These type erased functions cannot be used at compile-time.\n\n`scan_args` can be created with:\n\n`make_scan_args(format_str, ...args) -> internal scan_args_storage`\n\n- Returns an object that stores a format string with an array of all arguments to scan.\n- Keep in mind that the storage uses reference semantics and does not extend the lifetime of args. It is the\n  programmer's responsibility to ensure that args outlive the return value.\n\n### Scanner\n\nThere exists scanner for builtin types like char, string and integers. Support for other types (e.g. float) is planned.\n\nUse `is_scanner_v<Type>` to check if a type is scannable.\n\nA scanner exists of one optional function `validate` and two mandatory functions `parse` and `scan`. If `validate`\nis not present, `parse` must validate the format string.\n\n*Example*\n\n```cpp\nstruct foo {\n    int x;\n};\n\ntemplate <>\nclass emio::scanner<foo> {\n public:\n  /**\n   * Optional static function to validate the format string syntax for this type.\n   * @note If not present, the parse function is invoked for validation.\n   * @param format_rdr The reader over the format string.\n   * @return Success if the format string is valid.\n   */\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  /**\n   * Function to parse the format specs for this type.\n   * @param format_rdr The reader over the format string.\n   * @return Success if the format string is valid and could be parsed.\n   */\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  /**\n   * Function to scan the object of this type according to the parsed format specs.\n   * @param input The input reader.\n   * @param arg The argument to scan.\n   * @return Success if the scanning could be done.\n   */\n  constexpr result<void> scan(reader& input, foo& arg) const noexcept {\n    EMIO_TRYV(input.read_int(arg.x));\n    return success;\n  }\n};\n\nint main() {\n    foo f{};\n    emio::scan(\"42\", \"{}\", i);  // f.x == 42\n}\n```\n\nIt is also possible to reuse existing scanner via inheritance or composition.\n\n*Example*\n\n```cpp\nstruct foo {\n    int x;\n};\n\ntemplate <>\nclass emio::scanner<foo> : public emio::scanner<int> {\n public:\n  constexpr result<void> scan(reader& input, foo& arg) const noexcept {\n    return emio::scanner<int>::scan(input, arg.x);\n  }\n};\n\nint main() {\n    foo f{};\n    emio::scan(\"0x2A\", \"{:x}\", f);  // f.x == 42\n}\n```\n\nIf the `validate` (or if absent the `parse`) function is not constexpr, a runtime strings must be used. The `scan`\nfunction don't need to be constexpr if the scanning shouldn't be done at compile-time.\n"
  },
  {
    "path": "docs/CODE_OF_CONDUCT.md",
    "content": "# Code of Conduct\n\n* You will be judged by your contributions first, and your sense of humor second.\n* Nobody owes you anything.\n"
  },
  {
    "path": "docs/CONTRIBUTING.md",
    "content": "# Contributing\n\n<!--\n    Short overview, rules, general guidelines, notes about pull requests and\n    style should go here.\n-->\n\n## Code of Conduct\n\nPlease see the [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) document.\n\n## Getting started\n\nHelpful notes for developers can be found in the [`DEVELOPING.md`](DEVELOPING.md) document.\n"
  },
  {
    "path": "docs/DESIGN.md",
    "content": "# Design\n\nThis is a short design overview of *emio*.\n\n**Note:** [fmtlib/fmt](https://github.com/fmtlib/fmt) is an awesome library with great concepts and implementations\nwhich are partly reused in this library. All statements made here by comparing *emio* with *fmt* are only objective and\nshouldn't insult anybody's work.\n\n## Binary footprint\n\nThe API provides a low level API for reading or writing builtin-types and also a high-level API for parsing (TODO) or\nformatting, which reuses the low-level API. Additional, even when using the high-level API, only used types increase the\nbinary size. Furthermore, since the parse or format string can be validated at compile-time, the validation can be\nomitted at runtime.\n\nThis is e.g. different in *fmt*, which doesn't provide a low-level API. Also, *fmt* instantiates all builtin types (e.g.\nbool, int64_t, int128_t, long double...) even if only int32_t should be formatted. Last but not least, the format\nstring is validated at compile-time and runtime.\n\nTake the following code snippet as example which is compiled and statically linked.\n\n```cpp\nstd::string f = emio::format(\"{}\", 1);\n```\n\nIf compiled with *fmt*, **191 kBytes** of flash memory is required. If *emio* is used, only **5 kBytes** are requires.\nThis is **38 times** less! Keep in mind that flash memory of many microcontrollers is between 128 kBytes and 2 MBytes.\n\nThis huge advantage of *emio* comes with a price: *emio* doesn't support all features of *fmt*. But these features are\nlikely not so important for embedded systems. Some missing features are:\n\n- no std::locale support (no internationalization)\n- if a runtime format string is used, validation and parsing happens sequential (performance overhead)\n- some features cannot be API compatible and have to be done differently (e.g. make_format_args requires the format\n  string or dynamic width and precision is implemented by a wrapper object)\n\n## Result type\n\nInstead of C++-exceptions, `emio:result` is used for returning and propagating errors. It is similar to `std::expected`,\n`boost::outcome` or Rust's `Result`.\n\n`emio::result<T>` does either holds the expected value of type T or an unexpected error of type `emio::err`. Through\nobserver methods, the state and the value or the error can be visited.\n\n```cpp\nemio::result<std::string> format(...) noexcept;\n\nemio::result<char> get_first_digit_of_pi() noexcept {\n    emio::result<std::string> pi_as_str = format(\"{}\", M_PI);\n    if (pi_as_str) {\n        return pi_as_str->at(0);\n    }\n    return emio::err::invalid_data;\n}\n```\n\nTo reduce the if/else chain, emio provides two simple macros (similar to *boost::outcome*) `EMIO_TRY` and `EMIO_TRYV`.\nThe above function could be rewritten into:\n\n```cpp\nemio::result<std::string> format(...) noexcept;\n\nemio::result<char> get_first_digit_of_pi() noexcept {\n    EMIO_TRY(std::string pi_as_str, format(\"{}\", M_PI));\n    return pi_as_str.at(0);\n}\n```\n\nThe biggest advantage of using a result type is that error handling is still possible even if C++-exceptions are\ndisabled. Unlike other libraries like *fmt*, which mostly terminate deep inside their library if an error occurs, the\nresult object propagates the error back to the callee. The drawback is of course the small performance overhead and\nthe explicit handling of the control flow.\n\n## Performance\n\nThe current benchmarks show that the formatting is round about 1.5 - 2 times slower than fmtlib and the performance is\nsimilar to printf (e.g. for integer types). Scanning on the other hand is around twice as fast then scanf. See the\nbenchmark tests inside the CI for more details.\n\n## Class diagram\n\n![class diagram](res/class_diagram.png)\n\nSee also [API](API.md) notes."
  },
  {
    "path": "docs/DEVELOPING.md",
    "content": "# Contributing\n\nHere is some wisdom to help you build and test this project as a developer and potential contributor.\n\nThe project template is generated with [cmake-init](https://github.com/friendlyanon/cmake-init).\n\nIf you plan to contribute, please read the [CONTRIBUTING](CONTRIBUTING.md) guide.\n\n## Install\n\nOnly two commands are necessary to install this library on your system.\n\n```sh\ncmake -S . -B build\ncmake --install build\n```\n\n## Developer mode\n\nBuild system targets that are only useful for developers of this project are hidden if the `emio_DEVELOPER_MODE` option\nis disabled. Enabling this option makes tests and other developer targets and options available. Not enabling this\noption means that you are a consumer of this project and thus you have no need for these targets and options.\n\nDeveloper mode is always set to on in CI workflows.\n\n### Presets\n\nThis project makes use of [presets][1] to simplify the process of configuring the project. As a developer, you are\nrecommended to always have the [latest CMake version][2] installed to make use of the latest Quality-of-Life additions.\n\nYou have a few options to pass `emio_DEVELOPER_MODE` to the configure command, but this project prefers to use presets.\n\nAs a developer, you should create a `CMakeUserPresets.json` file at the root of the project:\n\n```json\n{\n  \"version\": 2,\n  \"cmakeMinimumRequired\": {\n    \"major\": 3,\n    \"minor\": 14,\n    \"patch\": 0\n  },\n  \"configurePresets\": [\n    {\n      \"name\": \"dev\",\n      \"binaryDir\": \"${sourceDir}/build/dev\",\n      \"inherits\": [\"dev-mode\", \"ci-<os>\"],\n      \"cacheVariables\": {\n        \"CMAKE_BUILD_TYPE\": \"Debug\"\n      }\n    }\n  ],\n  \"buildPresets\": [\n    {\n      \"name\": \"dev\",\n      \"configurePreset\": \"dev\",\n      \"configuration\": \"Debug\"\n    }\n  ],\n  \"testPresets\": [\n    {\n      \"name\": \"dev\",\n      \"configurePreset\": \"dev\",\n      \"configuration\": \"Debug\",\n      \"output\": {\n        \"outputOnFailure\": true\n      }\n    }\n  ]\n}\n```\n\nYou should replace `<os>` in your newly created presets file with the name of the operating system you have, which may\nbe `win64` or `unix`. You can see what these correspond to in the [`CMakePresets.json`](CMakePresets.json) file.\n\n`CMakeUserPresets.json` is also the perfect place in which you can put all sorts of things that you would otherwise want\nto pass to the configure command in the terminal.\n\n### Configure, build and test\n\nIf you followed the above instructions, then you can configure, build and test the project respectively with the\nfollowing commands from the project root on any operating system with any build system:\n\n```sh\ncmake --preset=dev\ncmake --build --preset=dev\nctest --preset=dev\n```\n\nPlease note that both the build and test command accepts a `-j` flag to specify the number of jobs to use, which should\nideally be specified to the number of threads your CPU has. You may also want to add that to your preset using the\n`jobs` property, see the [presets documentation][1] for more details.\n\n[1]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html\n\n[2]: https://cmake.org/download/\n"
  },
  {
    "path": "docs/res/class_diagram.puml",
    "content": "@startuml\n\nclass \"scan functions\" << (F,#FF7700) >> {\n    scan(input, format_str, args...) -> result<void>\n}\n\nclass \"format functions\" << (F,#FF7700) >> {\n    format(format_str, args...) -> result<std::string>\n}\n\nclass reader\nclass \"std::string_view\" as sv\nreader *-- sv\n\nabstract class buffer\n\nclass memory_buffer\nclass static_buffer\nclass span_buffer\nclass iterator_buffer\n\nbuffer <|-- iterator_buffer\nbuffer <|-- static_buffer\nbuffer <|-- span_buffer\nbuffer <|-- memory_buffer\n\nclass writer\nwriter o-- buffer\n\niterator_buffer o-- \"output iterator\"\n\npackage \"output iterator\" {\n    class truncating_iterator\n    class \"std::vector<T>::iterator\"\n    class \"const char *\"\n}\n\npackage \"contiguous container\" {\n    class \"std::array\"\n    class \"std::string\" as s2\n    class \"std::vector\"\n}\n\nspan_buffer o-- \"contiguous container\"\n\nclass format_string\n\nclass formatter<T - Type to format> {\n    validate(reader&) [0..1]\n    parse(reader&)\n    format(writer& out, T)\n}\n\nformat_string  -.> \"validate & parse\" formatter\n\n\"format functions\" *-.- \"format_string\"\n\"format functions\" -.-> \"writer\" : \"write formatted\\noutput\"\n\"format functions\" -.-> \"reader\" : read format\\nstring\n\"format functions\" -.-> \"formatter\" : parse,\\nformat\n\nclass format_scan_string\n\nclass scanner<T - Type to scan> {\n    validate(reader&) [0..1]\n    parse(reader&)\n    scan(reader& in, T&)\n}\n\nformat_scan_string   -.> \"validate & parse\" scanner\n\n\"scan functions\" *-.- \"format_scan_string\"\n\"scan functions\" -.-> \"reader\" : read input & \\nformat string\n\"scan functions\" -.-> \"scanner\" : parse,\\nscan\n\n@enduml"
  },
  {
    "path": "include/emio/buffer.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <iterator>\n#include <limits>\n#include <span>\n#include <utility>\n\n#if __STDC_HOSTED__\n#  include <string>\n#endif\n\n#include <string_view>\n#include <type_traits>\n\n#include \"detail/ct_vector.hpp\"\n#include \"result.hpp\"\n\nnamespace emio {\n\n/// The default cache size of buffers with an internal cache.\ninline constexpr size_t default_cache_size{128};\n\n/**\n * This class provides the basic API and functionality for receiving a contiguous memory region of chars to write into.\n * @note Use a specific subclass for a concrete instantiation.\n */\nclass buffer {\n public:\n  constexpr buffer(const buffer& other) = delete;\n  constexpr buffer(buffer&& other) = delete;\n  constexpr buffer& operator=(const buffer& other) = delete;\n  constexpr buffer& operator=(buffer&& other) = delete;\n  virtual constexpr ~buffer() noexcept = default;\n\n  /**\n   * Returns a write area with the requested size on success.\n   * @param size The size the write area should have.\n   * @return The write area with the requested size on success or eof if no write area is available.\n   */\n  constexpr result<std::span<char>> get_write_area_of(size_t size) noexcept {\n    EMIO_TRY(const std::span<char> area, get_write_area_of_max(size));\n    if (area.size() < size) {\n      used_ -= area.size();\n      return err::eof;\n    }\n    return area;\n  }\n\n  /**\n   * Returns a write area which may be smaller than the requested size.\n   * @note This function should be used to support subclasses with a limited internal buffer.\n   * E.g. Writing a long string in chunks.\n   * @param size The size the write area should maximal have.\n   * @return The write area with the requested size as maximum on success or eof if no write area is available.\n   */\n  constexpr result<std::span<char>> get_write_area_of_max(size_t size) noexcept {\n    // If there is enough remaining capacity in the current write area, return it.\n    // Otherwise, request a new write area from the concrete implementation.\n    // There is a special case for fixed size buffers. Since they cannot grow, they simply return the\n    // remaining capacity or EOF, if hitting zero capacity.\n\n    const size_t remaining_capacity = area_.size() - used_;\n    if (remaining_capacity >= size || fixed_size_ == fixed_size::yes) {\n      if (remaining_capacity == 0 && size != 0) {\n        return err::eof;\n      }\n      const size_t max_size = std::min(remaining_capacity, size);\n      const std::span<char> area = area_.subspan(used_, max_size);\n      used_ += max_size;\n      return area;\n    }\n    EMIO_TRY(const std::span<char> area, request_write_area(used_, size));\n    used_ += area.size();\n    return area;\n  }\n\n protected:\n  /// Flag to indicate if the buffer's size is fixed and cannot grow.\n  enum class fixed_size : bool { no, yes };\n\n  /**\n   * Constructs the buffer.\n   * @brief fixed Flag to indicate if the buffer's size is fixed and cannot grow.\n   */\n  constexpr explicit buffer(fixed_size fixed = fixed_size::no) noexcept : fixed_size_{fixed} {}\n\n  /**\n   * Requests a write area of the given size from a subclass.\n   * @param used Already written characters into the current write area.\n   * @param size The requested size of a new write area.\n   * @return The write area with the requested size as maximum on success or eof if no write area is available.\n   */\n  virtual constexpr result<std::span<char>> request_write_area(const size_t used, const size_t size) noexcept {\n    static_cast<void>(used);  // Keep params for documentation.\n    static_cast<void>(size);\n    return err::eof;\n  }\n\n  /**\n   * Sets a new write area in the base class object to use.\n   * @param area The new write area.\n   */\n  constexpr void set_write_area(const std::span<char> area) noexcept {\n    area_ = area;\n    used_ = 0;\n  }\n\n  /**\n   * Returns the count of written characters of the current hold write area.\n   * @return The count.\n   */\n  [[nodiscard]] constexpr size_t get_used_count() const noexcept {\n    return used_;\n  }\n\n private:\n  fixed_size fixed_size_{fixed_size::no};\n  size_t used_{};\n  std::span<char> area_{};\n};\n\n/**\n * This class fulfills the buffer API by providing an endless growing buffer.\n * @tparam StorageSize The size of the internal storage used for small buffer optimization.\n */\ntemplate <size_t StorageSize = default_cache_size>\nclass memory_buffer final : public buffer {\n public:\n  /**\n   * Constructs and initializes the buffer with the internal storage size.\n   */\n  constexpr memory_buffer() noexcept : memory_buffer{0} {}\n\n  /**\n   * Constructs and initializes the buffer with the given capacity.\n   * @param capacity The initial capacity.\n   */\n  constexpr explicit memory_buffer(const size_t capacity) noexcept {\n    // Request at least the internal storage size. Should never fail.\n    request_write_area(0, std::max(vec_.capacity(), capacity)).value();\n  }\n\n  constexpr memory_buffer(const memory_buffer& other)\n      : buffer{}, used_{other.used_ + other.get_used_count()}, vec_{other.vec_} {\n    this->set_write_area({vec_.data() + used_, vec_.data() + vec_.capacity()});\n  }\n\n  constexpr memory_buffer(memory_buffer&& other) noexcept\n      : buffer{}, used_{other.used_ + other.get_used_count()}, vec_{std::move(other).vec_} {\n    this->set_write_area({vec_.data() + used_, vec_.data() + vec_.capacity()});\n    other.reset();\n  }\n\n  constexpr memory_buffer& operator=(const memory_buffer& other) {\n    if (&other == this) {\n      return *this;\n    }\n\n    used_ = other.used_ + other.get_used_count();\n    vec_ = other.vec_;\n    this->set_write_area({vec_.data() + used_, vec_.data() + vec_.capacity()});\n    return *this;\n  }\n\n  constexpr memory_buffer& operator=(memory_buffer&& other) noexcept {\n    if (&other == this) {\n      return *this;\n    }\n\n    used_ = other.used_ + other.get_used_count();\n    vec_ = std::move(other).vec_;\n    this->set_write_area({vec_.data() + used_, vec_.data() + vec_.capacity()});\n    other.reset();\n    return *this;\n  }\n\n  constexpr ~memory_buffer() override = default;\n\n  /**\n   * Obtains a view over the underlying string object.\n   * @return The view.\n   */\n  [[nodiscard]] constexpr std::string_view view() const noexcept {\n    return {vec_.data(), used_ + this->get_used_count()};\n  }\n\n#if __STDC_HOSTED__\n  /**\n   * Obtains a copy of the underlying string object.\n   * @return The string.\n   */\n  [[nodiscard]] std::string str() const {\n    return std::string{view()};\n  }\n#endif\n\n  /**\n   * Resets the buffer's read and write position to the beginning of the internal storage.\n   */\n  constexpr void reset() noexcept {\n    used_ = 0;\n    vec_.clear();\n    request_write_area(0, vec_.capacity()).value();\n  }\n\n  /**\n   * Returns the number of chars that the buffer has currently allocated space for.\n   * @return The capacity.\n   */\n  [[nodiscard]] constexpr size_t capacity() const noexcept {\n    return vec_.capacity();\n  }\n\n protected:\n  constexpr result<std::span<char>> request_write_area(const size_t used, const size_t size) noexcept override {\n    const size_t new_size = vec_.size() + size;\n    vec_.reserve(new_size);\n    used_ += used;\n    const std::span<char> area{vec_.data() + used_, size};\n    this->set_write_area(area);\n    return area;\n  }\n\n private:\n  size_t used_{};\n  detail::ct_vector<char, StorageSize> vec_{};\n};\n\n/**\n * This class fulfills the buffer API by using a span over an contiguous range.\n */\nclass span_buffer : public buffer {\n public:\n  /**\n   * Constructs and initializes the buffer with an empty span.\n   */\n  constexpr span_buffer() : buffer{fixed_size::yes} {};\n\n  /**\n   * Constructs and initializes the buffer with the given span.\n   * @param span The span.\n   */\n  constexpr explicit span_buffer(const std::span<char> span) noexcept : buffer{fixed_size::yes}, span_{span} {\n    this->set_write_area(span_);\n  }\n\n  constexpr span_buffer(const span_buffer& other) : buffer{fixed_size::yes}, span_{other.span_} {\n    this->set_write_area(span_);\n    get_write_area_of(other.get_used_count()).value();\n  }\n\n  // NOLINTNEXTLINE(performance-move-constructor-init): optimized move not possible\n  constexpr span_buffer(span_buffer&& other) noexcept : span_buffer{std::as_const(other)} {}\n\n  constexpr span_buffer& operator=(const span_buffer& other) {\n    if (&other == this) {\n      return *this;\n    }\n\n    span_ = other.span_;\n    this->set_write_area(span_);\n    get_write_area_of(other.get_used_count()).value();\n    return *this;\n  }\n\n  constexpr span_buffer& operator=(span_buffer&& other) noexcept {\n    *this = std::as_const(other);\n    return *this;\n  }\n\n  constexpr ~span_buffer() override;\n\n  /**\n   * Obtains a view over the underlying string object.\n   * @return The view.\n   */\n  [[nodiscard]] constexpr std::string_view view() const noexcept {\n    return {span_.data(), this->get_used_count()};\n  }\n\n#if __STDC_HOSTED__\n  /**\n   * Obtains a copy of the underlying string object.\n   * @return The string.\n   */\n  [[nodiscard]] std::string str() const {\n    return std::string{view()};\n  }\n#endif\n\n  /**\n   * Resets the buffer's read and write position to the beginning of the span.\n   */\n  constexpr void reset() noexcept {\n    this->set_write_area(span_);\n  }\n\n  /**\n   * Returns the number of chars that the buffer has space for.\n   * @return The capacity.\n   */\n  [[nodiscard]] constexpr size_t capacity() const noexcept {\n    return span_.size();\n  }\n\n private:\n  std::span<char> span_;\n};\n\n// Out-of-line definition because of a GCC bug (93413). Fixed in GCC 13.\ninline constexpr span_buffer::~span_buffer() = default;\n\n/**\n * This class fulfills the buffer API by providing a fixed-size storage.\n * @tparam StorageSize The size of the storage.\n */\ntemplate <size_t StorageSize>\nclass static_buffer final : private std::array<char, StorageSize>, public span_buffer {\n public:\n  /**\n   * Constructs and initializes the buffer with the storage.\n   */\n  constexpr static_buffer() noexcept : span_buffer{std::span{*this}} {}\n\n  constexpr static_buffer(const static_buffer& other) : static_buffer() {\n    const std::span<char> area = get_write_area_of(other.get_used_count()).value();\n    detail::copy_n(&*other.begin(), area.size(), area.data());\n  }\n\n  // NOLINTNEXTLINE(performance-move-constructor-init): optimized move not possible\n  constexpr static_buffer(static_buffer&& other) noexcept : static_buffer(std::as_const(other)) {}\n\n  constexpr static_buffer& operator=(const static_buffer& other) {\n    if (&other == this) {\n      return *this;\n    }\n\n    set_write_area(std::span{*this});\n    const std::span<char> area = get_write_area_of(other.get_used_count()).value();\n    detail::copy_n(&*other.begin(), area.size(), area.data());\n    return *this;\n  }\n\n  constexpr static_buffer& operator=(static_buffer&& other) noexcept {\n    *this = std::as_const(other);\n    return *this;\n  }\n\n  constexpr ~static_buffer() override = default;\n\n  // Note: We inherit from std::array to put the storage lifetime before span_buffer.\n  // Clang will otherwise complain if the storage is a member variable and used during compile-time.\n};\n\nnamespace detail {\n\n// Extracts a reference to the container from back_insert_iterator.\ntemplate <typename Container>\nContainer& get_container(std::back_insert_iterator<Container> it) noexcept {\n  using bi_iterator = std::back_insert_iterator<Container>;\n  struct accessor : bi_iterator {\n    accessor(bi_iterator iter) : bi_iterator(iter) {}\n    using bi_iterator::container;\n  };\n  return *accessor{it}.container;\n}\n\n// Helper struct to get the value type of different iterators.\ntemplate <typename T>\nstruct get_value_type {\n  using type = typename std::iterator_traits<T>::value_type;\n};\n\ntemplate <typename Container>\nstruct get_value_type<std::back_insert_iterator<Container>> {\n  using type = typename Container::value_type;\n};\n\n#if __STDC_HOSTED__\ntemplate <typename Char, typename Traits>\nstruct get_value_type<std::ostreambuf_iterator<Char, Traits>> {\n  using type = Char;\n};\n#endif\n\ntemplate <typename T>\nusing get_value_type_t = typename get_value_type<T>::type;\n\ntemplate <typename InputIt, typename OutputIt>\nconstexpr auto copy_str(InputIt it, InputIt end, OutputIt out) -> OutputIt {\n  while (it != end) {\n    *out++ = static_cast<char>(*it++);\n  }\n  return out;\n}\n\n}  // namespace detail\n\n/**\n * This class template is used to create a buffer around different iterator types.\n */\ntemplate <typename Iterator, size_t CacheSize = default_cache_size>\nclass iterator_buffer;\n\n/**\n * This class fulfills the buffer API by using an output iterator and an internal cache.\n * @tparam Iterator The output iterator type.\n * @tparam CacheSize The size of the internal cache.\n */\ntemplate <typename Iterator, size_t CacheSize>\n  requires(std::input_or_output_iterator<Iterator> &&\n           std::output_iterator<Iterator, detail::get_value_type_t<Iterator>>)\nclass iterator_buffer<Iterator, CacheSize> final : public buffer {\n public:\n  /**\n   * Constructs and initializes the buffer with the given output iterator.\n   * @param it The output iterator.\n   */\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized\n  constexpr explicit iterator_buffer(Iterator it) noexcept : it_{it} {\n    this->set_write_area(cache_);\n  }\n\n  iterator_buffer(const iterator_buffer&) = delete;\n  iterator_buffer(iterator_buffer&&) = delete;\n  iterator_buffer& operator=(const iterator_buffer&) = delete;\n  iterator_buffer& operator=(iterator_buffer&&) = delete;\n  ~iterator_buffer() override = default;\n\n  /**\n   * Flushes the internal cache to the output iterator.\n   * @return Always succeeds.\n   */\n  constexpr result<void> flush() noexcept {\n    it_ = detail::copy_str(cache_.data(), cache_.data() + this->get_used_count(), it_);\n    this->set_write_area(cache_);\n    return success;\n  }\n\n  /**\n   * Flushes and returns the output iterator at the next write position.\n   * @return The output iterator.\n   */\n  constexpr Iterator out() noexcept {\n    flush().assume_value();  // Will never fail.\n    return it_;\n  }\n\n protected:\n  constexpr result<std::span<char>> request_write_area(const size_t /*used*/, const size_t size) noexcept override {\n    flush().assume_value();  // Will never fail.\n    const std::span<char> area{cache_};\n    this->set_write_area(area);\n    if (size > cache_.size()) {\n      return area;\n    }\n    return area.subspan(0, size);\n  }\n\n private:\n  Iterator it_;\n  std::array<char, CacheSize> cache_;\n};\n\n/**\n * This class fulfills the buffer API by using an output pointer.\n * @tparam Iterator The output iterator type.\n */\ntemplate <typename OutputPtr>\n  requires(std::input_or_output_iterator<OutputPtr*> &&\n           std::output_iterator<OutputPtr*, detail::get_value_type_t<OutputPtr*>>)\nclass iterator_buffer<OutputPtr*> final : public buffer {\n public:\n  /**\n   * Constructs and initializes the buffer with the given output pointer.\n   * @param ptr The output pointer.\n   */\n  constexpr explicit iterator_buffer(OutputPtr* ptr) noexcept : ptr_{ptr} {\n    this->set_write_area({ptr, std::numeric_limits<size_t>::max()});\n  }\n\n  iterator_buffer(const iterator_buffer&) = delete;\n  iterator_buffer(iterator_buffer&&) = delete;\n  iterator_buffer& operator=(const iterator_buffer&) = delete;\n  iterator_buffer& operator=(iterator_buffer&&) = delete;\n  ~iterator_buffer() override = default;\n\n  /**\n   * Does nothing. Kept for uniformity with other iterator_buffer implementations.\n   * @return Always succeeds.\n   */\n  constexpr result<void> flush() noexcept {\n    // Nothing.\n    return success;\n  }\n\n  /**\n   * Returns the output pointer at the next write position.\n   * @return The output pointer.\n   */\n  constexpr OutputPtr* out() noexcept {\n    return ptr_ + this->get_used_count();\n  }\n\n private:\n  OutputPtr* ptr_;\n};\n\n/**\n * This class fulfills the buffer API by using the container of an contiguous back-insert iterator.\n * @tparam Container The container type of the back-insert iterator.\n * @tparam Capacity The minimum initial requested capacity of the container.\n */\ntemplate <typename Container, size_t Capacity>\n  requires std::contiguous_iterator<typename Container::iterator>\nclass iterator_buffer<std::back_insert_iterator<Container>, Capacity> final : public buffer {\n public:\n  /**\n   * Constructs and initializes the buffer with the given back-insert iterator.\n   * @param it The back-insert iterator.\n   */\n  constexpr explicit iterator_buffer(std::back_insert_iterator<Container> it) noexcept\n      : container_{detail::get_container(it)} {\n    request_write_area(0, std::min(container_.capacity(), Capacity)).value();\n  }\n\n  iterator_buffer(const iterator_buffer&) = delete;\n  iterator_buffer(iterator_buffer&&) = delete;\n  iterator_buffer& operator=(const iterator_buffer&) = delete;\n  iterator_buffer& operator=(iterator_buffer&&) = delete;\n  ~iterator_buffer() override = default;\n\n  /**\n   * Flushes the back-insert iterator by adjusting the size.\n   * @return Always succeeds.\n   */\n  constexpr result<void> flush() noexcept {\n    container_.resize(used_ + this->get_used_count());\n    return success;\n  }\n\n  /**\n   * Flushes and returns the back-insert iterator.\n   * @return The back-insert iterator.\n   */\n  constexpr std::back_insert_iterator<Container> out() noexcept {\n    flush().assume_value();  // Will never fail.\n    return std::back_inserter(container_);\n  }\n\n protected:\n  constexpr result<std::span<char>> request_write_area(const size_t used, const size_t size) noexcept override {\n    const size_t new_size = container_.size() + size;\n    container_.resize(new_size);\n    used_ += used;\n    const std::span<char> area{container_.data() + used_, new_size};\n    this->set_write_area(area);\n    return area.subspan(0, size);\n  }\n\n private:\n  size_t used_{};\n  Container& container_;\n};\n\ntemplate <typename Iterator>\niterator_buffer(Iterator&&) -> iterator_buffer<std::decay_t<Iterator>>;\n\n/**\n * This class fulfills the buffer API by using a file stream and an internal cache.\n * @tparam CacheSize The size of the internal cache.\n */\ntemplate <size_t CacheSize = default_cache_size>\nclass file_buffer final : public buffer {\n public:\n  /**\n   * Constructs and initializes the buffer with the given file stream.\n   * @param file The file stream.\n   */\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized\n  constexpr explicit file_buffer(std::FILE* file) noexcept : file_{file} {\n    this->set_write_area(cache_);\n  }\n\n  file_buffer(const file_buffer&) = delete;\n  file_buffer(file_buffer&&) = delete;\n  file_buffer& operator=(const file_buffer&) = delete;\n  file_buffer& operator=(file_buffer&&) = delete;\n  ~file_buffer() override = default;\n\n  /**\n   * Flushes the internal cache to the file stream.\n   * @note Does not flush the file stream itself!\n   */\n  result<void> flush() noexcept {\n    const size_t written = std::fwrite(cache_.data(), sizeof(char), this->get_used_count(), file_);\n    if (written != this->get_used_count()) {\n      return err::eof;\n    }\n    this->set_write_area(cache_);\n    return success;\n  }\n\n  /**\n   * Resets the buffer's read and write position to the beginning of the file stream.\n   */\n  constexpr void reset() noexcept {\n    this->set_write_area(cache_);\n    std::fseek(file_, 0, SEEK_SET);\n  }\n\n protected:\n  result<std::span<char>> request_write_area(const size_t /*used*/, const size_t size) noexcept override {\n    EMIO_TRYV(flush());\n    const std::span<char> area{cache_};\n    this->set_write_area(area);\n    if (size > cache_.size()) {\n      return area;\n    }\n    return area.subspan(0, size);\n  }\n\n private:\n  std::FILE* file_;\n  std::array<char, CacheSize> cache_;\n};\n\n/**\n * This class fulfills the buffer API by using a primary buffer and an internal cache.\n * Only a limited amount of characters is written to the primary buffer. The remaining characters are truncated.\n * @tparam CacheSize The size of the internal cache.\n */\ntemplate <size_t CacheSize = default_cache_size>\nclass truncating_buffer final : public buffer {\n public:\n  /**\n   * Constructs and initializes the buffer with the given primary buffer and limit.\n   * @param primary The primary buffer.\n   * @param limit The limit.\n   */\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized.\n  constexpr explicit truncating_buffer(buffer& primary, size_t limit) : primary_{primary}, limit_{limit} {\n    this->set_write_area(cache_);\n  }\n\n  truncating_buffer(const truncating_buffer&) = delete;\n  truncating_buffer(truncating_buffer&&) = delete;\n  truncating_buffer& operator=(const truncating_buffer&) = delete;\n  truncating_buffer& operator=(truncating_buffer&&) = delete;\n  constexpr ~truncating_buffer() noexcept override = default;\n\n  /**\n   * Returns the count of the total (not truncated) written characters.\n   * @return The count.\n   */\n  [[nodiscard]] constexpr size_t count() const noexcept {\n    return used_ + this->get_used_count();\n  }\n\n  /**\n   * Flushes the internal cache to the primary buffer.\n   */\n  [[nodiscard]] constexpr result<void> flush() noexcept {\n    size_t bytes_to_write = get_used_count();\n    used_ += bytes_to_write;\n    while (written_ < limit_ && bytes_to_write > 0) {\n      EMIO_TRY(const auto area, primary_.get_write_area_of_max(std::min(bytes_to_write, limit_ - written_)));\n      detail::copy_n(detail::begin(cache_), area.size(), area.data());\n      written_ += area.size();\n      bytes_to_write -= area.size();\n    }\n    this->set_write_area(cache_);\n    return success;\n  }\n\n protected:\n  constexpr result<std::span<char>> request_write_area(const size_t /*used*/, const size_t size) noexcept override {\n    EMIO_TRYV(flush());\n    const std::span<char> area{cache_};\n    this->set_write_area(area);\n    if (size > cache_.size()) {\n      return area;\n    }\n    return area.subspan(0, size);\n  }\n\n private:\n  buffer& primary_;\n  size_t limit_;\n  size_t written_{};\n  size_t used_{};\n  std::array<char, CacheSize> cache_;\n};\n\nnamespace detail {\n\n/**\n * A buffer that counts the number of characters written. Discards the output.\n * @tparam CacheSize The size of the internal cache.\n */\n// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized.\ntemplate <size_t CacheSize = default_cache_size>\nclass counting_buffer final : public buffer {\n public:\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized.\n  constexpr counting_buffer() noexcept = default;\n  constexpr counting_buffer(const counting_buffer&) = delete;\n  constexpr counting_buffer(counting_buffer&&) noexcept = delete;\n  constexpr counting_buffer& operator=(const counting_buffer&) = delete;\n  constexpr counting_buffer& operator=(counting_buffer&&) noexcept = delete;\n  constexpr ~counting_buffer() noexcept override = default;\n\n  /**\n   * Calculates the number of Char's that were written.\n   * @return The number of Char's.\n   */\n  [[nodiscard]] constexpr size_t count() const noexcept {\n    return used_ + this->get_used_count();\n  }\n\n protected:\n  constexpr result<std::span<char>> request_write_area(const size_t used, const size_t size) noexcept override {\n    used_ += used;\n    const std::span<char> area{cache_};\n    this->set_write_area(area);\n    if (size > cache_.size()) {\n      return area;\n    }\n    return area.subspan(0, size);\n  }\n\n private:\n  size_t used_{};\n  std::array<char, CacheSize> cache_;\n};\n\n}  // namespace detail\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/detail/args.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <span>\n#include <string_view>\n#include <type_traits>\n\n#include \"validated_string_storage.hpp\"\n\nnamespace emio::detail {\n\n/**\n * Type erased argument to validate.\n */\ntemplate <template <typename> typename Trait>\nclass validation_arg {\n public:\n  template <typename T>\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): will be initialized in constructor\n  explicit validation_arg(std::type_identity<T> /*unused*/) noexcept {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): only way to use the storage\n    std::construct_at(reinterpret_cast<model_t<typename Trait<T>::unified_type>*>(&storage_));\n  }\n\n  validation_arg(const validation_arg&) = delete;\n  validation_arg(validation_arg&&) = delete;\n  validation_arg& operator=(const validation_arg&) = delete;\n  validation_arg& operator=(validation_arg&&) = delete;\n  // No destructor & delete call to concept_t because model_t holds only a reference.\n  ~validation_arg() = default;\n\n  result<void> validate(reader& format_rdr) const noexcept {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): only way to get the object back\n    return reinterpret_cast<const concept_t*>(&storage_)->validate(format_rdr);\n  }\n\n private:\n  class concept_t {\n   public:\n    concept_t() = default;\n    concept_t(const concept_t&) = delete;\n    concept_t(concept_t&&) = delete;\n    concept_t& operator=(const concept_t&) = delete;\n    concept_t& operator=(concept_t&&) = delete;\n\n    virtual result<void> validate(reader& format_rdr) const noexcept = 0;\n\n   protected:\n    ~concept_t() = default;\n  };\n\n  template <typename T>\n  class model_t final : public concept_t {\n   public:\n    explicit model_t() noexcept = default;\n    model_t(const model_t&) = delete;\n    model_t(model_t&&) = delete;\n    model_t& operator=(const model_t&) = delete;\n    model_t& operator=(model_t&&) = delete;\n\n    result<void> validate(reader& format_rdr) const noexcept override {\n      return Trait<std::remove_cvref_t<T>>::validate(format_rdr);\n    }\n\n   protected:\n    ~model_t() = default;\n  };\n\n  std::aligned_storage_t<sizeof(model_t<int>)> storage_;\n};\n\n/**\n * Type erased argument to parse and process.\n */\ntemplate <typename Input, template <typename> typename Trait>\nclass arg {\n public:\n  template <typename T>\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): will be initialized in constructor\n  explicit arg(T& value) noexcept {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): only way to use the storage\n    std::construct_at(reinterpret_cast<model_t<typename Trait<T>::unified_type>*>(&storage_), value);\n  }\n\n  arg(const arg&) = delete;\n  arg(arg&&) = delete;\n  arg& operator=(const arg&) = delete;\n  arg& operator=(arg&&) = delete;\n  ~arg() = default;  // No destructor & delete call to concept_t because model_t holds only a reference.\n\n  result<void> process_arg(Input& input, reader& format_rdr) const noexcept {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): only way to get the object back\n    return reinterpret_cast<const concept_t*>(&storage_)->process_arg(input, format_rdr);\n  }\n\n private:\n  class concept_t {\n   public:\n    concept_t() = default;\n    concept_t(const concept_t&) = delete;\n    concept_t(concept_t&&) = delete;\n    concept_t& operator=(const concept_t&) = delete;\n    concept_t& operator=(concept_t&&) = delete;\n\n    virtual result<void> process_arg(Input& input, reader& format_rdr) const noexcept = 0;\n\n   protected:\n    ~concept_t() = default;\n  };\n\n  template <typename T>\n  class model_t final : public concept_t {\n   public:\n    explicit model_t(T value) noexcept : value_{value} {}\n\n    model_t(const model_t&) = delete;\n    model_t(model_t&&) = delete;\n    model_t& operator=(const model_t&) = delete;\n    model_t& operator=(model_t&&) = delete;\n\n    result<void> process_arg(Input& input, reader& format_rdr) const noexcept override {\n      return Trait<std::remove_cvref_t<T>>::process_arg(input, format_rdr, value_);\n    }\n\n   protected:\n    ~model_t() = default;\n\n   private:\n    T value_;\n  };\n\n  std::aligned_storage_t<sizeof(model_t<std::string_view>)> storage_;\n};\n\ntemplate <typename Arg>\nclass args_span {\n public:\n  args_span() = default;\n  args_span(const args_span&) = delete;\n  args_span(args_span&&) = delete;\n  args_span& operator=(const args_span&) = delete;\n  args_span& operator=(args_span&&) = delete;\n  ~args_span() = default;\n\n  [[nodiscard]] std::span<const Arg> get_args() const noexcept {\n    return args_;\n  }\n\n protected:\n  args_span(std::span<const Arg> args) : args_{args} {}\n\n private:\n  std::span<const Arg> args_{};\n};\n\ntemplate <typename Arg>\nclass args_span_with_str : public args_span<Arg> {\n public:\n  args_span_with_str() = default;\n  args_span_with_str(const args_span_with_str&) = delete;\n  args_span_with_str(args_span_with_str&&) = delete;\n  args_span_with_str& operator=(const args_span_with_str&) = delete;\n  args_span_with_str& operator=(args_span_with_str&&) = delete;\n  ~args_span_with_str() = default;\n\n  /**\n   * Returns the validated format/scan string.\n   * @return The view or invalid_format if the validation failed.\n   */\n  [[nodiscard]] result<std::string_view> get_str() const noexcept {\n    return str_.get();\n  }\n\n  /**\n   * Returns if it is just a plain string without arguments.\n   * @return True, if the string does not contain any escape sequences, replacement fields or arguments, otherwise\n   * false.\n   */\n  [[nodiscard]] constexpr bool is_plain_str() const noexcept {\n    return str_.is_plain_str();\n  }\n\n  /**\n   * Returns if it is an empty string without arguments.\n   * @return True, if the string is empty without any arguments, otherwise false.\n   */\n  [[nodiscard]] constexpr bool empty() const noexcept {\n    return str_.empty();\n  }\n\n protected:\n  // NOLINTNEXTLINE(modernize-pass-by-value): false-positive since no dynamic allocation takes place\n  args_span_with_str(const validated_string_storage& str, std::span<const Arg> args)\n      : args_span<Arg>(args), str_{str} {}\n\n private:\n  validated_string_storage str_{};\n};\n\ntemplate <typename Arg, size_t NbrOfArgs>\nclass args_storage : public args_span_with_str<Arg> {\n public:\n  template <typename... Args>\n  // NOLINTNEXTLINE(modernize-pass-by-value): false-positive since no dynamic allocation takes place\n  args_storage(const validated_string_storage& str, Args&&... args) noexcept\n      : args_span_with_str<Arg>{str, args_storage_}, args_storage_{Arg{std::forward<Args>(args)}...} {}\n\n  args_storage(const args_storage&) = delete;\n  args_storage(args_storage&&) = delete;\n  args_storage& operator=(const args_storage&) = delete;\n  args_storage& operator=(args_storage&&) = delete;\n  ~args_storage() = default;\n\n private:\n  std::array<Arg, NbrOfArgs> args_storage_;\n};\n\ntemplate <typename Arg, size_t NbrOfArgs>\nclass validation_args_storage : public args_span<Arg> {\n public:\n  template <typename... Args>\n  validation_args_storage(Args&&... args) noexcept\n      : args_span<Arg>{args_storage_}, args_storage_{Arg{std::forward<Args>(args)}...} {}\n\n  validation_args_storage(const validation_args_storage&) = delete;\n  validation_args_storage(validation_args_storage&&) = delete;\n  validation_args_storage& operator=(const validation_args_storage&) = delete;\n  validation_args_storage& operator=(validation_args_storage&&) = delete;\n  ~validation_args_storage() = default;\n\n private:\n  std::array<Arg, NbrOfArgs> args_storage_;\n};\n\ntemplate <typename T, typename... Args>\nvalidation_args_storage<T, sizeof...(Args)> make_validation_args() noexcept {\n  return {std::type_identity<Args>{}...};\n}\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/detail/bignum.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n// This implementation is based on:\n// https://github.com/rust-lang/rust/blob/71ef9ecbdedb67c32f074884f503f8e582855c2f/library/core/src/num/bignum.rs\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstdint>\n#include <exception>\n#include <span>\n\n#include \"predef.hpp\"\n\nnamespace emio::detail {\n\nstruct carrying_add_result_t {\n  uint32_t value;\n  bool carry;\n\n  friend constexpr bool operator==(const carrying_add_result_t& lhs,\n                                   const carrying_add_result_t& rhs) noexcept = default;\n};\n\ninline constexpr carrying_add_result_t carrying_add(uint32_t a, uint32_t b, bool carry) noexcept {\n  const uint32_t v1 = a + b;\n  const bool carry1 = v1 < a;\n  const uint32_t v2 = v1 + static_cast<uint32_t>(carry);\n  const bool carry2 = v2 < v1;\n  return {v2, carry1 || carry2};\n}\n\nstruct borrowing_sub_result_t {\n  uint32_t value;\n  bool borrow;\n\n  friend constexpr bool operator==(const borrowing_sub_result_t& lhs,\n                                   const borrowing_sub_result_t& rhs) noexcept = default;\n};\n\ninline constexpr borrowing_sub_result_t borrowing_sub(uint32_t a, uint32_t b, bool borrow) noexcept {\n  const uint32_t v1 = a - b;\n  const bool borrow1 = v1 > a;\n  const uint32_t v2 = v1 - static_cast<uint32_t>(borrow);\n  const bool borrow2 = v2 > v1;\n  return {v2, borrow1 || borrow2};\n}\n\nstruct carrying_mul_result_t {\n  uint32_t value;\n  uint32_t carry;\n\n  friend constexpr bool operator==(const carrying_mul_result_t& lhs,\n                                   const carrying_mul_result_t& rhs) noexcept = default;\n};\n\ninline constexpr carrying_mul_result_t carrying_mul(uint32_t a, uint32_t b, uint32_t carry) noexcept {\n  const uint64_t v1 = static_cast<uint64_t>(a) * b + carry;\n  const auto v2 = static_cast<uint32_t>(v1);\n  const auto carry1 = static_cast<uint32_t>(v1 >> 32U);\n  return {v2, carry1};\n}\n\n/// Stack-allocated arbitrary-precision (up to certain limit) integer.\nclass bignum {\n public:\n  static constexpr size_t max_blocks = 34;\n\n  static constexpr bignum from(size_t sz, const std::array<uint32_t, max_blocks>& b) noexcept {\n    bignum bn{};\n    bn.size_ = sz;\n    bn.base_ = b;\n    return bn;\n  }\n\n  constexpr explicit bignum() noexcept = default;\n\n  /// Makes a bignum from one digit.\n  template <typename T>\n    requires(std::is_unsigned_v<T> && sizeof(T) <= sizeof(uint32_t))\n  constexpr explicit bignum(T v) noexcept : base_{{v}} {}\n\n  /// Makes a bignum from `u64` value.\n  template <typename T>\n    requires(std::is_unsigned_v<T> && sizeof(T) == sizeof(uint64_t))\n  constexpr explicit bignum(T v) noexcept : base_{{static_cast<uint32_t>(v), static_cast<uint32_t>(v >> 32)}} {\n    size_ += static_cast<size_t>(base_[1] > 0);\n  }\n\n  /// Returns the internal digits as a slice `[a, b, c, ...]` such that the numeric\n  /// value is `a + b * 2^W + c * 2^(2W) + ...` where `W` is the number of bits in\n  /// the digit type.\n  constexpr std::span<uint32_t> digits() noexcept {\n    return {base_.data(), size_};\n  }\n\n  /// Returns the `i`-th bit where bit 0 is the least significant one.\n  /// In other words, the bit with weight `2^i`.\n  [[nodiscard]] constexpr uint8_t get_bit(size_t i) const noexcept {\n    const size_t digitbits = 32;\n    const auto d = i / digitbits;\n    const auto b = i % digitbits;\n    return static_cast<uint8_t>((base_[d] >> b) & 1U);\n  }\n\n  /// Returns `true` if the bignum is zero.\n  [[nodiscard]] constexpr bool is_zero() const noexcept {\n    return std::all_of(base_.begin(), base_.end(), [](uint32_t v) {\n      return v == 0;\n    });\n  }\n\n  // add\n  // add_small\n  constexpr bignum& add_small(uint32_t other) noexcept {\n    return add_small_at(0, other);\n  }\n\n  constexpr bignum& add_small_at(size_t index, uint32_t other) noexcept {\n    size_t i = index;\n    auto res = carrying_add(base_[i], other, false);\n    base_[i] = res.value;\n    i += 1;\n    for (; res.carry && (i < base_.size()); i++) {\n      res = carrying_add(base_[i], 0, res.carry);\n      base_[i] = res.value;\n    }\n    EMIO_Z_DEV_ASSERT(!res.carry);\n    size_ = i;\n    return *this;\n  }\n\n  constexpr bignum& add(const bignum& other) noexcept {\n    carrying_add_result_t res{0, false};\n    size_t i = 0;\n    for (; (i < other.size_) || (res.carry && (i < base_.size())); i++) {\n      res = carrying_add(base_[i], other.base_[i], res.carry);\n      base_[i] = res.value;\n    }\n    EMIO_Z_DEV_ASSERT(!res.carry);\n    if (i > size_) {\n      size_ = i;\n    }\n    return *this;\n  }\n\n  /// Subtracts `other` from itself and returns its own mutable reference.\n  constexpr bignum& sub_small(uint32_t other) noexcept {\n    auto res = borrowing_sub(base_[0], other, false);\n    base_[0] = res.value;\n    size_t i = 1;\n    for (; res.borrow && (i < base_.size()); i++) {\n      res = borrowing_sub(base_[i], 0, res.borrow);\n      base_[i] = res.value;\n    }\n    EMIO_Z_DEV_ASSERT(!res.borrow);\n    if (i == size_ && size_ != 1) {\n      size_ -= 1;\n    }\n    return *this;\n  }\n\n  /// Subtracts `other` from itself and returns its own mutable reference.\n  constexpr bignum& sub(const bignum& other) noexcept {\n    EMIO_Z_DEV_ASSERT(size_ >= other.size_);\n    if (size_ == 0) {\n      return *this;\n    }\n    borrowing_sub_result_t res{0, false};\n    for (size_t i = 0; i < size_; i++) {\n      res = borrowing_sub(base_[i], other.base_[i], res.borrow);\n      base_[i] = res.value;\n    }\n    EMIO_Z_DEV_ASSERT(!res.borrow);\n    do {\n      if (base_[size_ - 1] != 0) {\n        break;\n      }\n    } while (--size_ != 0);\n    return *this;\n  }\n\n  /// Multiplies itself by a digit-sized `other` and returns its own\n  /// mutable reference.\n  constexpr bignum& mul_small(uint32_t other) noexcept {\n    return muladd_small(other, 0);\n  }\n\n  constexpr bignum& muladd_small(uint32_t other, uint32_t carry) noexcept {\n    carrying_mul_result_t res{0, carry};\n    for (size_t i = 0; i < size_; i++) {\n      res = carrying_mul(base_[i], other, res.carry);\n      base_[i] = res.value;\n    }\n    if (res.carry > 0) {\n      base_[size_] = res.carry;\n      size_ += 1;\n    }\n    return *this;\n  }\n\n  [[nodiscard]] bignum mul(const bignum& other) const noexcept {\n    const auto& bn_max = size_ > other.size_ ? *this : other;\n    const auto& bn_min = size_ > other.size_ ? other : *this;\n\n    bignum prod{};\n    for (size_t i = 0; i < bn_min.size_; i++) {\n      carrying_mul_result_t res{0, 0};\n      for (size_t j = 0; j < bn_max.size_; j++) {\n        res = carrying_mul(bn_min.base_[i], bn_max.base_[j], res.carry);\n        prod.add_small_at(i + j, res.value);\n      }\n      if (res.carry > 0) {\n        prod.add_small_at(i + bn_max.size_, res.carry);\n      }\n    }\n    return prod;\n  }\n\n  constexpr bignum& mul_digits(std::span<const uint32_t> other) noexcept {\n    const auto& bn_max = size_ > other.size() ? digits() : other;\n    const auto& bn_min = size_ > other.size() ? other : digits();\n\n    bignum prod{};\n    for (size_t i = 0; i < bn_min.size(); i++) {\n      carrying_mul_result_t res{0, 0};\n      for (size_t j = 0; j < bn_max.size(); j++) {\n        res = carrying_mul(bn_min[i], bn_max[j], res.carry);\n        prod.add_small_at(i + j, res.value);\n      }\n      if (res.carry > 0) {\n        prod.add_small_at(i + bn_max.size(), res.carry);\n      }\n    }\n    *this = prod;\n    return *this;\n  }\n\n  /// Multiplies itself by `5^e` and returns its own mutable reference.\n  constexpr bignum& mul_pow5(size_t k) noexcept {\n    // Multiply with the largest single-digit power as long as possible.\n    while (k >= 13) {\n      mul_small(1220703125);\n      k -= 13;\n    }\n    // Stop if nothing left.\n    if (k == 0) {\n      return *this;\n    }\n    // Finish off the remainder.\n    uint32_t rest_power{5};\n    while (--k > 0) {\n      rest_power *= 5;\n    }\n    return mul_small(rest_power);\n  }\n\n  /// Divides itself by a digit-sized `other` and returns its own\n  /// mutable reference *and* the remainder.\n  constexpr uint32_t div_rem_small(uint32_t other) noexcept {\n    uint64_t borrow = 0;\n    for (size_t i = size_; i > 0; i--) {\n      const uint64_t v = (base_[i - 1] + (borrow << 32U));\n      const uint64_t res = v / other;\n      base_[i - 1] = static_cast<uint32_t>(res);\n      borrow = v - res * other;\n    }\n    return static_cast<uint32_t>(borrow);\n  }\n\n  /// Multiplies itself by `2^exp` and returns its own mutable reference.\n  constexpr bignum& mul_pow2(size_t exp) noexcept {\n    const size_t digits = exp / 32;\n    const size_t bits = exp % 32;\n\n    if (digits > 0) {\n      for (size_t i = size_; i > 0; --i) {\n        base_[i + digits - 1] = base_[i - 1];\n      }\n      for (size_t i = 0; i < digits; i++) {\n        base_[i] = 0;\n      }\n      size_ += digits;\n    }\n    if (bits > 0) {\n      uint32_t overflow = 0;\n      size_t i = 0;\n      for (; i < size_; i++) {\n        auto res = static_cast<uint64_t>(base_[i]) << bits;\n        base_[i] = static_cast<uint32_t>(res) + overflow;\n        overflow = static_cast<uint32_t>(res >> 32);\n      }\n      if (overflow > 0) {\n        base_[i] = overflow;\n        size_ += 1;\n      }\n    }\n    return *this;\n  }\n\n  [[nodiscard]] constexpr std::strong_ordering operator<=>(const bignum& other) const noexcept {\n    if (size_ > other.size_) {\n      return std::strong_ordering::greater;\n    }\n    if (size_ < other.size_) {\n      return std::strong_ordering::less;\n    }\n    for (size_t i = size_; i > 0; i--) {\n      if (base_[i - 1] > other.base_[i - 1]) {\n        return std::strong_ordering::greater;\n      }\n      if (base_[i - 1] < other.base_[i - 1]) {\n        return std::strong_ordering::less;\n      }\n    }\n    return std::strong_ordering::equal;\n  }\n\n  constexpr bool operator==(const bignum& other) const noexcept = default;\n\n private:\n  /// Number of \"digits\" used in base_.\n  size_t size_{1};\n  /// Digits. `[a, b, c, ...]` represents `a + b*2^W + c*2^(2W) + ...`\n  /// where `W` is the number of bits in the digit type.\n  std::array<uint32_t, max_blocks> base_{};\n};\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/detail/bitset.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <array>\n#include <bit>\n#include <exception>\n#include <limits>\n\nnamespace emio::detail {\n\n/**\n * A constexpr bitset with the bare minimum implementation.\n * @tparam Bits The number of bits.\n */\ntemplate <size_t Bits>\nclass bitset {\n private:\n  using word_t = size_t;\n  static constexpr size_t bits_per_word = sizeof(word_t) * 8;\n  static constexpr size_t number_of_words = (Bits / bits_per_word) + (((Bits % bits_per_word) == 0) ? 0 : 1);\n\n public:\n  /**\n   * Checks if all bits are set to true.\n   * @return true if all bits are set to true, otherwise false\n   */\n  [[nodiscard]] constexpr bool all() const noexcept {\n    if constexpr (Bits <= 0) {\n      return true;\n    } else {\n      for (size_t i = 0; i < number_of_words - 1; i++) {\n        if (words_[i] != ~word_t{0}) {\n          return false;\n        }\n      }\n      constexpr word_t high_word_mask = get_high_word_mask();\n      return words_[number_of_words - 1] == high_word_mask;\n    }\n  }\n\n  /**\n   * Checks if the first n bits are set to true.\n   * @param n - number of bits\n   * @return true if the first n bits are set to true, otherwise false\n   */\n  [[nodiscard, gnu::noinline]] constexpr bool all_first(size_t n) const noexcept {\n    // Prevent inlining because of a GCC compiler-bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106921\n    if constexpr (Bits <= 0) {\n      return n == 0;\n    } else {\n      if (n > Bits) {\n        return false;\n      }\n      size_t i = 0;\n      for (; n > bits_per_word; n -= bits_per_word, i++) {\n        if (words_[i] != ~word_t{0}) {  // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index): ensured by loop\n          return false;\n        }\n      }\n      word_t last_word = words_[i];  // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index): ensured by loop\n      for (; n != 0; n--) {\n        if ((last_word & 1) != 1) {\n          return false;\n        }\n        last_word >>= 1;\n      }\n      return true;\n    }\n  }\n\n  /**\n   * Returns the number of bits that the bitset holds.\n   * @return the number of bits\n   */\n  [[nodiscard]] constexpr size_t size() const noexcept {\n    return Bits;\n  }\n\n  /**\n   * Sets a specific bit to true.\n   * @param pos - the position of the bit\n   */\n  constexpr void set(size_t pos) noexcept {\n    if (pos >= Bits) {\n      std::terminate();\n    }\n    // Get index of pos in words and truncate pos to word bits per word.\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index): ensured by check before\n    words_[pos / bits_per_word] |= word_t{1} << (pos % bits_per_word);\n  }\n\n private:\n  static constexpr word_t get_high_word_mask() noexcept {\n    word_t high_word_mask = (word_t{1} << (Bits % (bits_per_word))) - word_t{1};\n    if (high_word_mask == 0) {\n      return std::numeric_limits<word_t>::max();\n    }\n    return high_word_mask;\n  }\n\n  std::array<word_t, number_of_words> words_{};\n};\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/detail/conversion.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <bit>\n#include <cstdint>\n#include <cstring>\n#include <limits>\n#include <optional>\n#include <string_view>\n#include <type_traits>\n\n#include \"predef.hpp\"\n\nnamespace emio::detail {\n\nconstexpr bool isalpha(char c) {\n  return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');\n}\n\nconstexpr bool isdigit(char c) {\n  return (c >= '0' && c <= '9');\n}\n\nconstexpr bool is_valid_number_base(const int base) noexcept {\n  return base >= 2 && base <= 36;\n}\n\nconstexpr std::optional<int> char_to_digit(const char c, const int base) noexcept {\n  if (c < '0') {\n    return std::nullopt;\n  }\n  int res{};\n  if (c >= 'a') {\n    res = c - 'a' + 10;\n  } else if (c >= 'A') {\n    res = c - 'A' + 10;\n  } else {\n    res = c - '0';\n  }\n  if (res < base) {\n    return res;\n  }\n  return std::nullopt;\n}\n\nconstexpr char digit_to_char(const int digit, bool upper) noexcept {\n  if (digit >= 10) {\n    EMIO_Z_DEV_ASSERT(digit < 36);\n    if (upper) {\n      return static_cast<char>(static_cast<int>('A') + (digit - 10));\n    }\n    return static_cast<char>(static_cast<int>('a') + (digit - 10));\n  }\n  EMIO_Z_DEV_ASSERT(digit < 10);\n  return static_cast<char>(static_cast<int>('0') + digit);\n}\n\ntemplate <typename T>\n  requires(std::is_unsigned_v<T>)\nconstexpr size_t count_digits_10(T number) noexcept {\n  size_t count = 1;\n  for (;;) {\n    // Integer division is slow so do it for a group of four digits instead\n    // of for every digit. The idea comes from the talk by Alexandrescu\n    // \"Three Optimization Tips for C++\".\n    if (number < 10) {\n      return count;\n    }\n    if (number < 100) {\n      return count + 1;\n    }\n    if (number < 1000) {\n      return count + 2;\n    }\n    if (number < 10000) {\n      return count + 3;\n    }\n    number /= 10000U;\n    count += 4;\n  }\n}\n\ntemplate <size_t Base, typename T>\n  requires(std::is_unsigned_v<T>)\nconstexpr size_t count_digits(T number) noexcept {\n  if (number == 0) {\n    return 1;\n  }\n\n  if constexpr (Base == 10) {\n    return count_digits_10(number);\n  } else if constexpr (Base == 2) {\n    return static_cast<size_t>(std::bit_width(number));\n  } else if constexpr (Base == 8) {\n    return static_cast<size_t>((std::bit_width(number) + 2) / 3);\n  } else if constexpr (Base == 16) {\n    return static_cast<size_t>(((std::bit_width(number) + 3) / 4));\n  } else {\n    size_t digit_cnt{1};\n    for (number /= static_cast<T>(Base); number; number /= static_cast<T>(Base)) {\n      ++digit_cnt;\n    }\n    return digit_cnt;\n  }\n}\n\ntemplate <typename T>\n  requires(std::is_unsigned_v<T>)\nconstexpr size_t get_number_of_digits(T number, int base) noexcept {\n  if (number == 0) {\n    return 1;\n  }\n  if (base == 10) {\n    return count_digits<10>(number);\n  }\n  if (base == 16) {\n    return count_digits<16>(number);\n  }\n  if (base == 2) {\n    return count_digits<2>(number);\n  }\n  if (base == 8) {\n    return count_digits<8>(number);\n  }\n  size_t digit_cnt{1};\n  for (number /= static_cast<T>(base); number; number /= static_cast<T>(base)) {\n    ++digit_cnt;\n  }\n  return digit_cnt;\n}\n\ntemplate <typename T>\nconstexpr bool is_negative(T value) noexcept {\n  if constexpr (std::is_signed_v<T>) {\n    return value < 0;\n  } else {\n    return false;\n  }\n}\n\ntemplate <typename T>\nconstexpr int num_bits() noexcept {\n  return std::numeric_limits<T>::digits;\n}\n\ntemplate <typename T>\nusing int32_or_64 = std::conditional_t<num_bits<T>() <= 32, int32_t, int64_t>;\n\ntemplate <typename T>\nusing uint32_or_64 = std::conditional_t<num_bits<T>() <= 32, uint32_t, uint64_t>;\n\ntemplate <typename T>\nusing upcasted_int_t = std::conditional_t<std::is_signed_v<T>, int32_or_64<T>, uint32_or_64<T>>;\n\ntemplate <typename T>\n  requires(std::is_integral_v<T>)\nconstexpr auto integer_upcast(T integer) noexcept {\n  return static_cast<upcasted_int_t<T>>(integer);\n}\n\ntemplate <typename T>\nconstexpr uint32_or_64<T> to_absolute(T number) noexcept {\n  if constexpr (std::is_unsigned_v<T>) {\n    return number;\n  } else {\n    if (is_negative(number)) {\n      auto abs = static_cast<uint32_or_64<T>>(number);\n      abs = T{} - abs;\n      return abs;\n    }\n    return static_cast<uint32_or_64<T>>(number);\n  }\n}\n\ntemplate <typename T>\nconstexpr std::make_unsigned_t<T> to_unsigned(T number) noexcept {\n  return static_cast<std::make_unsigned_t<T>>(number);\n}\n\ntemplate <typename T>\nconstexpr std::make_signed_t<T> to_signed(T number) noexcept {\n  return static_cast<std::make_signed_t<T>>(number);\n}\n\n// Converts value in the range [0, 100) to a string.\ninline constexpr const char* digits2(size_t value) noexcept {\n  // GCC generates slightly better code when value is pointer-size.\n  return &\"0001020304050607080910111213141516171819\"\n      \"2021222324252627282930313233343536373839\"\n      \"4041424344454647484950515253545556575859\"\n      \"6061626364656667686970717273747576777879\"\n      \"8081828384858687888990919293949596979899\"[value * 2];\n}\n\n// Copies two characters from src to dst.\ntemplate <typename Char>\ninline constexpr void copy2(Char* dst, const char* src) noexcept {\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    *dst++ = static_cast<Char>(*src++);\n    *dst = static_cast<Char>(*src);\n  } else {\n    memcpy(dst, src, 2);\n  }\n}\n\ntemplate <typename T>\n  requires(std::is_unsigned_v<T>)\nconstexpr char* write_decimal(T abs_number, char* next) noexcept {\n  // Write number from right to left.\n  while (abs_number >= 100) {\n    next -= 2;\n    copy2(next, digits2(static_cast<size_t>(abs_number % 100)));\n    abs_number /= 100;\n  }\n  if (abs_number < 10) {\n    *--next = '0' + static_cast<char>(abs_number);\n    return next;\n  }\n  next -= 2;\n  copy2(next, digits2(static_cast<size_t>(abs_number)));\n  return next;\n}\n\ntemplate <size_t BaseBits, typename T>\n  requires(std::is_unsigned_v<T>)\nconstexpr char* write_uint(T abs_number, const bool upper, char* next) noexcept {\n  const char* digits = upper ? \"0123456789ABCDEF\" : \"0123456789abcdef\";\n  do {\n    T digit = static_cast<T>(abs_number & ((1 << BaseBits) - 1));\n    if constexpr (BaseBits < 4) {\n      EMIO_Z_DEV_ASSERT(digit < 8);\n      *--next = static_cast<char>('0' + digit);\n    } else {\n      EMIO_Z_DEV_ASSERT(digit < 16);\n      *--next = digits[digit];\n    }\n  } while ((abs_number >>= BaseBits) != 0);\n  return next;\n}\n\ntemplate <typename T>\n  requires(std::is_unsigned_v<T>)\nconstexpr char* write_number(T abs_number, const int base, const bool upper, char* next) noexcept {\n  if (base == 10) {\n    return write_decimal(abs_number, next);\n  }\n  if (base == 16) {\n    return write_uint<4>(abs_number, upper, next);\n  }\n  if (base == 2) {\n    return write_uint<1>(abs_number, false, next);\n  }\n  if (base == 8) {\n    return write_uint<3>(abs_number, false, next);\n  }\n  if (abs_number == 0) {\n    *(--next) = '0';\n    return next;\n  }\n  // Write number from right to left.\n  for (; abs_number; abs_number /= static_cast<T>(base)) {\n    const char c = digit_to_char(static_cast<int>(abs_number % static_cast<T>(base)), upper);\n    *(--next) = c;\n  }\n  return next;\n}\n\ninline constexpr size_t npos = std::string_view::npos;\n\nconstexpr std::string_view unchecked_substr(const std::string_view& str, size_t pos, size_t n = npos) noexcept {\n  const size_t rlen = std::min(n, str.length() - pos);\n  return {str.data() + pos, rlen};\n}\n\ntemplate <typename Size>\nconstexpr char* fill_n(char* out, Size count, char value) noexcept {\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    for (Size i = 0; i < count; i++) {\n      *out++ = value;\n    }\n    return out;\n  } else {\n    std::memset(out, value, to_unsigned(count));\n    return out + count;\n  }\n}\n\ntemplate <typename Size>\nconstexpr char* copy_n(const char* in, Size count, char* out) noexcept {\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    for (Size i = 0; i < count; i++) {\n      *out++ = *in++;\n    }\n    return out;\n  } else {\n    std::memcpy(out, in, to_unsigned(count));\n    return out + count;\n  }\n}\n\n[[nodiscard]] inline constexpr bool equal_n(const char* a, const char* b, const size_t n) {\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    for (size_t i = 0; i < n; i++) {\n      if (a[i] != b[i]) {\n        return false;\n      }\n    }\n    return true;\n  } else {\n    return std::memcmp(a, b, n) == 0;\n  }\n}\n\nusing namespace std::string_view_literals;\n\n// Helper function to construct string literals directly as string_view during compilation if string_view_literal\n// operator \"\" sv is not available.\ninline consteval std::string_view sv(std::string_view sv) noexcept {\n  return sv;\n}\n\n// T::iterator and T::const_iterator are not guaranteed to be pointers.\n// But emio uses pointers to char* and const char*. Therefore, these helper functions are needed.\n\ntemplate <typename T>\nconcept is_iterable = requires(T&& t) {\n  t.begin() != t.end();                    // begin/end and operator !=\n  ++std::declval<decltype(t.begin())&>();  // operator ++\n  *t.begin();                              // operator*\n};\n\ntemplate <typename T>\n  requires is_iterable<T>\nconstexpr auto begin(T& obj) noexcept {\n  if constexpr (std::is_same_v<std::remove_pointer_t<typename T::iterator>, char>) {\n    return obj.begin();\n  } else {\n    return &*obj.begin();\n  }\n}\n\ntemplate <typename T>\n  requires is_iterable<T>\nconstexpr auto end(T& obj) noexcept {\n  if constexpr (std::is_same_v<std::remove_pointer_t<typename T::iterator>, char>) {\n    return obj.end();\n  } else {\n    return &*obj.end();\n  }\n}\n\ntemplate <typename T>\n  requires is_iterable<T>\nconstexpr const char* begin(const T& obj) noexcept {\n  if constexpr (std::is_same_v<std::remove_pointer_t<typename T::const_iterator>, char>) {\n    return obj.begin();\n  } else {\n    return &*obj.begin();\n  }\n}\n\ntemplate <typename T>\n  requires is_iterable<T>\nconstexpr const char* end(const T& obj) noexcept {\n  if constexpr (std::is_same_v<std::remove_pointer_t<typename T::const_iterator>, char>) {\n    return obj.end();\n  } else {\n    return &*obj.end();\n  }\n}\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/detail/ct_vector.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n\n#include \"conversion.hpp\"\n#include \"predef.hpp\"\n\nnamespace emio::detail {\n\n/**\n * A constexpr vector with the bare minimum implementation and inlined storage.\n * @tparam Char The character type.\n * @tparam StorageSize The size of the inlined storage.\n */\ntemplate <typename Char, size_t StorageSize = 128>\nclass ct_vector {\n public:\n  constexpr ct_vector() noexcept {\n    if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n      fill_n(storage_.data(), storage_.size(), 0);\n    }\n  }\n\n  constexpr ct_vector(const ct_vector& other) : ct_vector() {\n    reserve(other.size_);\n    copy_n(other.data_, other.size_, data_);\n  }\n\n  constexpr ct_vector(ct_vector&& other) noexcept : ct_vector() {\n    // Transfer ownership.\n    if (other.hold_external()) {\n      data_ = other.data_;\n      capacity_ = other.capacity_;\n    } else {\n      copy_n(other.data_, other.size_, data_);\n    }\n    size_ = other.size_;\n\n    // Reset other.\n    other.data_ = other.storage_.data();\n    other.size_ = 0;\n    other.capacity_ = StorageSize;\n  }\n\n  constexpr ct_vector& operator=(const ct_vector& other) {\n    if (&other == this) {\n      return *this;\n    }\n    reserve(other.size_);\n    copy_n(other.data_, other.size_, data_);\n    return *this;\n  }\n\n  constexpr ct_vector& operator=(ct_vector&& other) noexcept {\n    if (&other == this) {\n      return *this;\n    }\n\n    // Free this.\n    if (hold_external()) {\n      delete[] data_;  // NOLINT(cppcoreguidelines-owning-memory)\n    }\n\n    // Transfer ownership.\n    if (other.hold_external()) {\n      data_ = other.data_;\n      capacity_ = other.capacity_;\n    } else {\n      copy_n(other.data_, other.size_, data_);\n    }\n    size_ = other.size_;\n\n    // Reset other.\n    other.data_ = other.storage_.data();\n    other.size_ = 0;\n    other.capacity_ = StorageSize;\n    return *this;\n  }\n\n  constexpr ~ct_vector() noexcept {\n    if (hold_external()) {\n      delete[] data_;  // NOLINT(cppcoreguidelines-owning-memory)\n    }\n  }\n\n  constexpr void reserve(size_t new_size) noexcept {\n    if (new_size < StorageSize && !hold_external()) {\n      size_ = new_size;\n      return;\n    }\n\n    // Heavy pointer arithmetic because high level containers are not yet ready to use at constant evaluation.\n    if (capacity_ < new_size) {\n      // NOLINTNEXTLINE(bugprone-unhandled-exception-at-new): char types cannot throw\n      Char* new_data = new Char[new_size];  // NOLINT(cppcoreguidelines-owning-memory)\n      copy_n(data_, size_, new_data);\n      if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n        // Required at compile-time because another reserve could happen without previous write to the data.\n        fill_n(new_data + size_, new_size - size_, 0);\n      }\n      std::swap(new_data, data_);\n      capacity_ = new_size;\n      if (new_data != storage_.data()) {\n        delete[] new_data;  // NOLINT(cppcoreguidelines-owning-memory)\n      }\n    }\n    size_ = new_size;\n  }\n\n  constexpr void clear() noexcept {\n    size_ = 0;\n  }\n\n  [[nodiscard]] constexpr size_t capacity() const noexcept {\n    return capacity_;\n  }\n\n  [[nodiscard]] constexpr size_t size() const noexcept {\n    return size_;\n  }\n\n  [[nodiscard]] constexpr Char* data() noexcept {\n    return data_;\n  }\n\n  [[nodiscard]] constexpr const Char* data() const noexcept {\n    return data_;\n  }\n\n private:\n  [[nodiscard]] constexpr bool hold_external() const noexcept {\n    return data_ != storage_.data() && data_ != nullptr;\n  }\n\n  std::array<Char, StorageSize> storage_;\n  Char* data_{storage_.data()};\n  size_t size_{};\n  size_t capacity_{StorageSize};\n};\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/detail/format/args.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"../../formatter.hpp\"\n#include \"../args.hpp\"\n\nnamespace emio::detail::format {\n\ntemplate <typename Arg>\nstruct format_arg_trait {\n  using unified_type = format::unified_type_t<std::remove_const_t<Arg>>;\n\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return detail::format::validate_trait<Arg>(format_rdr);\n  }\n\n  static constexpr result<void> process_arg(writer& out, reader& format_rdr, const Arg& arg) noexcept {\n    formatter<Arg> formatter;\n    EMIO_TRYV(formatter.parse(format_rdr));\n    return formatter.format(out, arg);\n  }\n};\n\nusing format_validation_arg = validation_arg<format_arg_trait>;\n\nusing format_arg = arg<writer, format_arg_trait>;\n\nusing format_args = args_span_with_str<format_arg>;\n\n}  // namespace emio::detail::format\n"
  },
  {
    "path": "include/emio/detail/format/decode.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n// This implementation is based on:\n// https://github.com/rust-lang/rust/blob/71ef9ecbdedb67c32f074884f503f8e582855c2f/library/core/src/num/flt2dec/decoder.rs\n\n#pragma once\n\n#include <bit>\n#include <cstdint>\n#include <cstring>\n#include <limits>\n#include <type_traits>\n\nnamespace emio::detail::format {\n\nstruct finite_result_t {\n  uint64_t mant{};\n  uint64_t minus{};\n  uint64_t plus{};\n  int16_t exp{};\n  bool inclusive{};\n};\n\nenum class category { zero, finite, infinity, nan };\n\nstruct decode_result_t {\n  bool negative{};\n  format::category category{};\n  finite_result_t finite{};  // Only valid if category is finite.\n};\n\ninline constexpr decode_result_t decode(double value) noexcept {\n  decode_result_t res{};\n\n  using bits_type = uint64_t;\n  const auto bits = std::bit_cast<bits_type>(value);\n\n  res.negative = bits >> 63 != 0;\n  if (value == 0) {\n    return res;\n  }\n\n  // Exponent bias + mantissa shift\n  res.finite.exp = static_cast<int16_t>(((bits >> 52) & 0x7ff) - (1023 + 52));\n  res.finite.mant = res.finite.exp == -1075 ? (bits & 0xfffffffffffff) << 1 : (bits & 0xfffffffffffff);\n  res.finite.inclusive = (res.finite.mant & 1) == 0;\n\n  if (res.finite.exp == 972) {  // non-numbers.\n    if (res.finite.mant == 0) {\n      res.category = category::infinity;\n    } else {\n      res.category = category::nan;\n    }\n  } else {\n    res.category = category::finite;\n    res.finite.minus = 1;\n    res.finite.plus = 1;\n    if (res.finite.exp != -1075) {  // Norm.\n      res.finite.mant |= 0x10000000000000;\n      constexpr auto minnorm = std::bit_cast<bits_type>(std::numeric_limits<double>::min());\n      if (res.finite.mant == minnorm) {\n        res.finite.plus = 2;\n        res.finite.mant <<= 2;\n        res.finite.exp -= 2;\n      } else {\n        res.finite.mant <<= 1;\n        res.finite.exp -= 1;\n      }\n    }\n  }\n\n  return res;\n}\n\n}  // namespace emio::detail::format\n"
  },
  {
    "path": "include/emio/detail/format/dragon.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n// This implementation is based on:\n// https://github.com/rust-lang/rust/blob/71ef9ecbdedb67c32f074884f503f8e582855c2f/library/core/src/num/flt2dec/strategy/dragon.rs\n\n#pragma once\n\n#include <algorithm>\n#include <bit>\n#include <cstdint>\n#include <cstring>\n#include <limits>\n#include <optional>\n#include <span>\n#include <type_traits>\n\n#include \"../../buffer.hpp\"\n#include \"../bignum.hpp\"\n#include \"decode.hpp\"\n\nnamespace emio::detail::format {\n\ninline constexpr int16_t estimate_scaling_factor(uint64_t mant, int16_t exp) noexcept {\n  // 2^(nbits-1) < mant <= 2^nbits if mant > 0\n  const int nbits = 64 - std::countl_zero(mant - 1);\n  // 1292913986 = floor(2^32 * log_10 2)\n  // therefore this always underestimates (or is exact), but not much.\n  return static_cast<int16_t>((static_cast<int64_t>(nbits + exp) * 1292913986) >> 32);\n}\n\ninline constexpr std::optional<char> round_up(std::span<char> d) noexcept {\n  const auto end = d.rend();\n  auto it = std::find_if(d.rbegin(), end, [](char c) {\n    return c != '9';\n  });\n  if (it != end) {\n    // d[i+1..n] is all nines.\n    auto i = static_cast<size_t>(std::distance(it, end) - 1);\n    d[i] += 1;  // Round up.\n    for (size_t j = i + 1; j < d.size(); j++) {\n      d[j] = '0';\n    }\n    return std::nullopt;\n  } else if (!d.empty()) {\n    // 999..999 rounds to 1000..000 with an increased exponent\n    d[0] = '1';\n    for (char& c : d.subspan(1)) {\n      c = '0';\n    }\n    return '0';\n  }\n  // an empty buffer rounds up (a bit strange but reasonable)\n  return '1';\n}\n\nstruct format_fp_result_t {\n  std::span<const char> digits;\n  int16_t exp;\n};\n\nenum class format_exact_mode { significand_digits, decimal_point };\n\ninline constexpr format_fp_result_t format_exact(const finite_result_t& dec, emio::buffer& buf, format_exact_mode mode,\n                                                 int16_t number_of_digits) noexcept {\n  EMIO_Z_DEV_ASSERT(dec.mant > 0);\n  EMIO_Z_DEV_ASSERT(dec.minus > 0);\n  EMIO_Z_DEV_ASSERT(dec.plus > 0);\n\n  // estimate `k_0` from original inputs satisfying `10^(k_0-1) < v <= 10^(k_0+1)`.\n  int16_t k = estimate_scaling_factor(dec.mant, dec.exp);\n\n  // `v = mant / scale`.\n  auto mant = bignum(dec.mant);\n  auto scale = bignum(1U);\n\n  size_t s2 = 0;\n  size_t s5 = 0;\n  size_t m2 = 0;\n  size_t m5 = 0;\n\n  if (dec.exp < 0) {\n    s2 = static_cast<size_t>(-dec.exp);\n  } else {\n    m2 += static_cast<size_t>(dec.exp);\n  }\n\n  // divide `mant` by `10^k`. now `scale / 10 < mant <= scale * 10`.\n  if (k >= 0) {\n    s2 += static_cast<size_t>(k);\n    s5 += static_cast<size_t>(k);\n  } else {\n    m2 += static_cast<size_t>(-k);\n    m5 += static_cast<size_t>(-k);\n  }\n\n  scale.mul_pow5(s5);\n  scale.mul_pow2(s2);\n\n  mant.mul_pow5(m5);\n  mant.mul_pow2(m2);\n\n  // calculate required buffer size\n  size_t len{};\n  size_t extra_len{};\n  if (mode == format_exact_mode::significand_digits) {\n    len = static_cast<size_t>(number_of_digits);\n  } else if ((k + number_of_digits) >= 0) {\n    len = static_cast<size_t>(k + number_of_digits);\n    extra_len = 1;\n  }\n\n  // fixup estimation\n  // in order to keep the fixed-size bignum, we actually use `mant + floor(plus) >= scale`.\n  // we are not actually modifying `scale`, since we can skip the initial multiplication instead.\n  // again with the shortest algorithm, `d[0]` can be zero but will be eventually rounded up.\n  // if we are working with the last-digit limitation, we need to shorten the buffer\n  // before the actual rendering in order to avoid double rounding.\n  // note that we have to enlarge the buffer again when rounding up happens!\n  if (mant >= scale) {\n    k += 1;\n    len += extra_len;\n  } else {\n    mant.mul_small(10);\n  }\n\n  auto dst = buf.get_write_area_of(len).value();\n\n  if (len > 0) {\n    // cache `(2, 4, 8) * scale` for digit generation.\n    bignum scale2 = scale;\n    scale2.mul_pow2(1);\n    bignum scale4 = scale;\n    scale4.mul_pow2(2);\n    bignum scale8 = scale;\n    scale8.mul_pow2(3);\n\n    for (size_t i = 0; i < len; i++) {\n      if (mant.is_zero()) {\n        // following digits are all zeroes, we stop here\n        // do *not* try to perform rounding! rather, fill remaining digits.\n        for (char& c : dst.subspan(i)) {\n          c = '0';\n        }\n        return {dst, k};\n      }\n\n      size_t d = 0;\n      if (mant >= scale8) {\n        mant.sub(scale8);\n        d += 8;\n      }\n      if (mant >= scale4) {\n        mant.sub(scale4);\n        d += 4;\n      }\n      if (mant >= scale2) {\n        mant.sub(scale2);\n        d += 2;\n      }\n      if (mant >= scale) {\n        mant.sub(scale);\n        d += 1;\n      }\n      EMIO_Z_DEV_ASSERT(mant < scale);\n      EMIO_Z_DEV_ASSERT(d < 10);\n      dst[i] = static_cast<char>('0' + d);\n      mant.mul_small(10);\n    }\n  }\n\n  // rounding up if we stop in the middle of digits\n  // if the following digits are exactly 5000..., check the prior digit and try to\n  // round to even (i.e., avoid rounding up when the prior digit is even).\n  const auto order = mant <=> (scale.mul_small(5));\n  if (order == std::strong_ordering::greater ||\n      (order == std::strong_ordering::equal && len > 0 && (dst[len - 1] & 1) == 1)) {\n    // if rounding up changes the length, the exponent should also change.\n    // but we've been requested a fixed number of digits, so do not alter the buffer...\n    if (std::optional<char> c = round_up(dst.subspan(0, len))) {\n      k += 1;\n      // ...unless we've been requested the fixed precision instead.\n      // we also need to check that, if the original buffer was empty,\n      // the additional digit can only be added when `k == limit` (edge case).\n      if (k > -number_of_digits) {\n        if (len == 0) {\n          dst = buf.get_write_area_of(1).value();\n        }\n\n        if (len != 0 && len < dst.size()) {\n          return {};\n        }\n\n        if (len < dst.size()) {\n          dst[len] = *c;\n          len += 1;\n        }\n      }\n    }\n  }\n  return {dst.subspan(0, len), k};\n}\n\ninline constexpr format_fp_result_t format_shortest(const finite_result_t& dec, emio::buffer& buf) noexcept {\n  // the number `v` to format is known to be:\n  // - equal to `mant * 2^exp`;\n  // - preceded by `(mant - 2 * minus) * 2^exp` in the original type; and\n  // - followed by `(mant + 2 * plus) * 2^exp` in the original type.\n  //\n  // obviously, `minus` and `plus` cannot be zero. (for infinities, we use out-of-range values.)\n  // also we assume that at least one digit is generated, i.e., `mant` cannot be zero too.\n  //\n  // this also means that any number between `low = (mant - minus) * 2^exp` and\n  // `high = (mant + plus) * 2^exp` will map to this exact floating point number,\n  // with bounds included when the original mantissa was even (i.e., `!mant_was_odd`).\n  EMIO_Z_DEV_ASSERT(dec.mant > 0);\n  EMIO_Z_DEV_ASSERT(dec.minus > 0);\n  EMIO_Z_DEV_ASSERT(dec.plus > 0);\n  //  EMIO_Z_DEV_ASSERT(buf.() >= MAX_SIG_DIGITS);\n\n  // `a.cmp(&b) < rounding` is `if d.inclusive {a <= b} else {a < b}`\n  const auto rounding = [&](std::strong_ordering ordering) noexcept {\n    if (dec.inclusive) {\n      return ordering <= 0;  // NOLINT(modernize-use-nullptr): false positive\n    }\n    return ordering < 0;  // NOLINT(modernize-use-nullptr): false positive\n  };\n\n  // estimate `k_0` from original inputs satisfying `10^(k_0-1) < high <= 10^(k_0+1)`.\n  // the tight bound `k` satisfying `10^(k-1) < high <= 10^k` is calculated later.\n  int16_t k = estimate_scaling_factor(dec.mant + dec.plus, dec.exp);\n\n  // convert `{mant, plus, minus} * 2^exp` into the fractional form so that:\n  // - `v = mant / scale`\n  // - `low = (mant - minus) / scale`\n  // - `high = (mant + plus) / scale`\n  auto mant = bignum(dec.mant);\n  auto minus = bignum(dec.minus);\n  auto plus = bignum(dec.plus);\n  auto scale = bignum(1U);\n\n  size_t s2 = 0;\n  size_t s5 = 0;\n  size_t m2 = 0;\n  size_t m5 = 0;\n\n  if (dec.exp < 0) {\n    s2 = static_cast<size_t>(-dec.exp);\n  } else {\n    m2 += static_cast<size_t>(dec.exp);\n  }\n\n  // divide `mant` by `10^k`. now `scale / 10 < mant + plus <= scale * 10`.\n  if (k >= 0) {\n    s2 += static_cast<size_t>(k);\n    s5 += static_cast<size_t>(k);\n  } else {\n    m2 += static_cast<size_t>(-k);\n    m5 += static_cast<size_t>(-k);\n  }\n\n  scale.mul_pow5(s5);\n  scale.mul_pow2(s2);\n\n  mant.mul_pow5(m5);\n  mant.mul_pow2(m2);\n  minus.mul_pow5(m5);\n  minus.mul_pow2(m2);\n  plus.mul_pow5(m5);\n  plus.mul_pow2(m2);\n\n  // fixup when `mant + plus > scale` (or `>=`).\n  // we are not actually modifying `scale`, since we can skip the initial multiplication instead.\n  // now `scale < mant + plus <= scale * 10` and we are ready to generate digits.\n  //\n  // note that `d[0]` *can* be zero, when `scale - plus < mant < scale`.\n  // in this case rounding-up condition (`up` below) will be triggered immediately.\n  if (rounding(scale <=> (bignum{mant}.add(plus)))) {\n    // equivalent to scaling `scale` by 10\n    k += 1;\n  } else {\n    mant.mul_small(10);\n    minus.mul_small(10);\n    plus.mul_small(10);\n  }\n\n  // cache `(2, 4, 8) * scale` for digit generation.\n  bignum scale2 = scale;\n  scale2.mul_pow2(1);\n  bignum scale4 = scale;\n  scale4.mul_pow2(2);\n  bignum scale8 = scale;\n  scale8.mul_pow2(3);\n\n  auto dst = buf.get_write_area_of(std::numeric_limits<double>::max_digits10).value();\n\n  bool down{};\n  bool up{};\n  size_t i{};\n  while (true) {\n    // invariants, where `d[0..n-1]` are digits generated so far:\n    // - `v = mant / scale * 10^(k-n-1) + d[0..n-1] * 10^(k-n)`\n    // - `v - low = minus / scale * 10^(k-n-1)`\n    // - `high - v = plus / scale * 10^(k-n-1)`\n    // - `(mant + plus) / scale <= 10` (thus `mant / scale < 10`)\n    // where `d[i..j]` is a shorthand for `d[i] * 10^(j-i) + ... + d[j-1] * 10 + d[j]`.\n\n    // generate one digit: `d[n] = floor(mant / scale) < 10`.\n    size_t d = 0;\n    if (mant >= scale8) {\n      mant.sub(scale8);\n      d += 8;\n    }\n    if (mant >= scale4) {\n      mant.sub(scale4);\n      d += 4;\n    }\n    if (mant >= scale2) {\n      mant.sub(scale2);\n      d += 2;\n    }\n    if (mant >= scale) {\n      mant.sub(scale);\n      d += 1;\n    }\n    EMIO_Z_DEV_ASSERT(mant < scale);\n    EMIO_Z_DEV_ASSERT(d < 10);\n    dst[i] = static_cast<char>('0' + d);\n    i += 1;\n\n    // this is a simplified description of the modified Dragon algorithm.\n    // many intermediate derivations and completeness arguments are omitted for convenience.\n    //\n    // start with modified invariants, as we've updated `n`:\n    // - `v = mant / scale * 10^(k-n) + d[0..n-1] * 10^(k-n)`\n    // - `v - low = minus / scale * 10^(k-n)`\n    // - `high - v = plus / scale * 10^(k-n)`\n    //\n    // assume that `d[0..n-1]` is the shortest representation between `low` and `high`,\n    // i.e., `d[0..n-1]` satisfies both of the following but `d[0..n-2]` doesn't:\n    // - `low < d[0..n-1] * 10^(k-n) < high` (bijectivity: digits round to `v`); and\n    // - `abs(v / 10^(k-n) - d[0..n-1]) <= 1/2` (the last digit is correct).\n    //\n    // the second condition simplifies to `2 * mant <= scale`.\n    // solving invariants in terms of `mant`, `low` and `high` yields\n    // a simpler version of the first condition: `-plus < mant < minus`.\n    // since `-plus < 0 <= mant`, we have the correct shortest representation\n    // when `mant < minus` and `2 * mant <= scale`.\n    // (the former becomes `mant <= minus` when the original mantissa is even.)\n    //\n    // when the second doesn't hold (`2 * mant > scale`), we need to increase the last digit.\n    // this is enough for restoring that condition: we already know that\n    // the digit generation guarantees `0 <= v / 10^(k-n) - d[0..n-1] < 1`.\n    // in this case, the first condition becomes `-plus < mant - scale < minus`.\n    // since `mant < scale` after the generation, we have `scale < mant + plus`.\n    // (again, this becomes `scale <= mant + plus` when the original mantissa is even.)\n    //\n    // in short:\n    // - stop and round `down` (keep digits as is) when `mant < minus` (or `<=`).\n    // - stop and round `up` (increase the last digit) when `scale < mant + plus` (or `<=`).\n    // - keep generating otherwise.\n    down = rounding(mant <=> (minus));\n    up = rounding(scale <=> (bignum{mant}.add(plus)));\n    if (down || up) {\n      // we have the shortest representation, proceed to the rounding\n      break;\n    }\n\n    // restore the invariants.\n    // this makes the algorithm always terminating: `minus` and `plus` always increases,\n    // but `mant` is clipped modulo `scale` and `scale` is fixed.\n    mant.mul_small(10);\n    minus.mul_small(10);\n    plus.mul_small(10);\n  }\n\n  // rounding up happens when\n  // i) only the rounding-up condition was triggered, or\n  // ii) both conditions were triggered and tie breaking prefers rounding up.\n  if (up && (!down || mant.mul_pow2(1) >= scale)) {\n    // if rounding up changes the length, the exponent should also change.\n    // it seems that this condition is very hard to satisfy (possibly impossible),\n    // but we are just being safe and consistent here.\n    // SAFETY: we initialized that memory above.\n    if (std::optional<char> c = round_up(dst.subspan(0, i))) {\n      dst[i] = *c;\n      i += 1;\n      k += 1;\n    }\n  }\n  return {dst.subspan(0, i), k};\n}\n\n}  // namespace emio::detail::format\n"
  },
  {
    "path": "include/emio/detail/format/format_to.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"../../reader.hpp\"\n#include \"../../writer.hpp\"\n#include \"../validated_string.hpp\"\n#include \"args.hpp\"\n#include \"formatter.hpp\"\n#include \"parser.hpp\"\n\nnamespace emio {\n\nnamespace detail::format {\n\nstruct format_trait {\n  template <typename... Args>\n  [[nodiscard]] static constexpr bool validate_string(std::string_view format_str) noexcept {\n    if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n      return validate<format_specs_checker>(format_str, sizeof...(Args), std::type_identity<Args>{}...);\n    } else {\n      return validate<format_specs_checker>(\n          format_str, sizeof...(Args), related_format_args{make_validation_args<format_validation_arg, Args...>()});\n    }\n  }\n};\n\ntemplate <typename... Args>\nusing format_string = validated_string<format_trait, std::type_identity_t<Args>...>;\n\ntemplate <typename... Args>\nusing valid_format_string = valid_string<format_trait, std::type_identity_t<Args>...>;\n\n// Non constexpr version.\ninline result<void> vformat_to(buffer& buf, const format_args& args) noexcept {\n  EMIO_TRY(const std::string_view str, args.get_str());\n  writer wtr{buf};\n  if (args.is_plain_str()) {\n    return wtr.write_str(str);\n  }\n  return parse<format_parser>(str, wtr, related_format_args{args});\n}\n\n// Constexpr version.\ntemplate <typename... Args>\nconstexpr result<void> format_to(buffer& buf, const format_string<Args...>& format_string,\n                                 const Args&... args) noexcept {\n  EMIO_TRY(const std::string_view str, format_string.get());\n  writer wtr{buf};\n  if (format_string.is_plain_str()) {\n    return wtr.write_str(str);\n  }\n  return parse<format_parser>(str, wtr, args...);\n}\n\n}  // namespace detail::format\n\n/**\n * Formatter for format_args.\n */\ntemplate <>\nclass formatter<detail::format::format_args> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  static constexpr result<void> parse(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  static result<void> format(writer& out, const detail::format::format_args& arg) noexcept {\n    return detail::format::vformat_to(out.get_buffer(), arg);\n  }\n\n  static constexpr bool format_can_fail = true;\n};\n\n/**\n * Formatter for types which inherit from format_args.\n */\ntemplate <typename T>\n  requires(std::is_base_of_v<detail::format::format_args, T>)\nclass formatter<T> : public formatter<detail::format::format_args> {};\n\nnamespace detail::format {\n\ntemplate <typename T>\n  requires(std::is_base_of_v<detail::format::format_args, T>)\nstruct unified_type<T> {\n  using type = const detail::format::format_args&;\n};\n\n}  // namespace detail::format\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/detail/format/formatter.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"../../reader.hpp\"\n#include \"../../writer.hpp\"\n#include \"../misc.hpp\"\n#include \"dragon.hpp\"\n#include \"specs.hpp\"\n\nnamespace emio {\n\ntemplate <typename>\nclass formatter;\n\nnamespace detail::format {\n\nnamespace alternate_form {\n\ninline constexpr std::string_view bin_lower{\"0b\"};\ninline constexpr std::string_view bin_upper{\"0B\"};\ninline constexpr std::string_view octal{\"0\"};\ninline constexpr std::string_view octal_lower{\"0o\"};\ninline constexpr std::string_view octal_upper{\"0O\"};\ninline constexpr std::string_view hex_lower{\"0x\"};\ninline constexpr std::string_view hex_upper{\"0X\"};\n\n}  // namespace alternate_form\n\n//\n// Write args.\n//\n\ninline constexpr result<void> write_padding_left(writer& out, format_specs& specs, size_t width) noexcept {\n  if (specs.width == 0 || specs.width < static_cast<int>(width)) {\n    specs.width = 0;\n    return success;\n  }\n  int fill_width = specs.width - static_cast<int>(width);\n  if (specs.align == alignment::left) {\n    specs.width = fill_width;\n    return success;\n  }\n  if (specs.align == alignment::center) {\n    fill_width = fill_width / 2;\n  }\n  specs.width -= fill_width + static_cast<int>(width);\n  return out.write_char_n(specs.fill, static_cast<size_t>(fill_width));\n}\n\ninline constexpr result<void> write_padding_right(writer& out, format_specs& specs) noexcept {\n  if (specs.width == 0 || (specs.align != alignment::left && specs.align != alignment::center)) {\n    return success;\n  }\n  return out.write_char_n(specs.fill, static_cast<size_t>(specs.width));\n}\n\ntemplate <alignment DefaultAlign, typename Func>\nconstexpr result<void> write_padded(writer& out, format_specs& specs, size_t width, const Func& func) noexcept {\n  if (specs.align == alignment::none) {\n    specs.align = DefaultAlign;\n  }\n  EMIO_TRYV(write_padding_left(out, specs, width));\n  EMIO_TRYV(func());\n  return write_padding_right(out, specs);\n}\n\ninline constexpr result<std::pair<std::string_view, writer::write_int_options>> make_write_int_options(\n    char spec_type) noexcept {\n  using namespace alternate_form;\n\n  std::string_view prefix;\n  writer::write_int_options options{};\n\n  switch (spec_type) {\n  case no_type:\n  case 'd':\n    options.base = 10;\n    break;\n  case 'x':\n    prefix = hex_lower;\n    options.base = 16;\n    break;\n  case 'X':\n    prefix = hex_upper;\n    options = {.base = 16, .upper_case = true};\n    break;\n  case 'b':\n    prefix = bin_lower;\n    options.base = 2;\n    break;\n  case 'B':\n    prefix = bin_upper;\n    options.base = 2;\n    break;\n  case 'o':\n    prefix = octal;\n    options.base = 8;\n    break;\n  default:\n    return err::invalid_format;\n  }\n  return std::pair{prefix, options};\n}\n\ninline constexpr result<char> try_write_sign(writer& out, const format_specs& specs, bool is_negative) noexcept {\n  char sign_to_write = no_sign;\n  if (is_negative) {\n    sign_to_write = '-';\n  } else if (specs.sign == '+' || specs.sign == ' ') {\n    sign_to_write = specs.sign;\n  }\n  if (sign_to_write != no_sign && specs.zero_flag) {\n    EMIO_TRYV(out.write_char(sign_to_write));\n    return no_sign;\n  }\n  return sign_to_write;\n}\n\ninline constexpr result<std::string_view> try_write_prefix(writer& out, const format_specs& specs,\n                                                           std::string_view prefix) noexcept {\n  const bool write_prefix = specs.alternate_form && !prefix.empty();\n  if (write_prefix && specs.zero_flag) {\n    EMIO_TRYV(out.write_str(prefix));\n    return \"\"sv;\n  }\n  if (write_prefix) {\n    return prefix;\n  }\n  return \"\"sv;\n}\n\ntemplate <typename Arg>\n  requires(std::is_integral_v<Arg> && !std::is_same_v<Arg, bool> && !std::is_same_v<Arg, char>)\nconstexpr result<void> write_arg(writer& out, format_specs& specs, const Arg& arg) noexcept {\n  if (specs.type == 'c') {\n    return write_padded<alignment::left>(out, specs, 1, [&]() noexcept {\n      return out.write_char(static_cast<char>(arg));\n    });\n  }\n  EMIO_TRY((auto [prefix, options]), make_write_int_options(specs.type));\n\n  if (specs.type == 'o' && arg == 0) {\n    prefix = \"\"sv;\n  }\n\n  const auto abs_number = detail::to_absolute(arg);\n  const bool is_negative = detail::is_negative(arg);\n  const size_t num_digits = detail::get_number_of_digits(abs_number, options.base);\n\n  EMIO_TRY(const char sign_to_write, try_write_sign(out, specs, is_negative));\n  EMIO_TRY(const std::string_view prefix_to_write, try_write_prefix(out, specs, prefix));\n\n  size_t total_width = num_digits;\n  if (specs.alternate_form) {\n    total_width += prefix.size();\n  }\n  if (is_negative || specs.sign == ' ' || specs.sign == '+') {\n    total_width += 1;\n  }\n\n  return write_padded<alignment::right>(out, specs, total_width, [&, &opt = options]() noexcept -> result<void> {\n    const size_t area_size = num_digits + static_cast<size_t>(sign_to_write != no_sign) + prefix_to_write.size();\n    EMIO_TRY(auto area, out.get_buffer().get_write_area_of(area_size));\n    auto* it = area.data();\n    if (sign_to_write != no_sign) {\n      *it++ = sign_to_write;\n    }\n    if (!prefix_to_write.empty()) {\n      it = copy_n(prefix_to_write.data(), prefix_to_write.size(), it);\n    }\n    write_number(abs_number, opt.base, opt.upper_case, it + detail::to_signed(num_digits));\n    return success;\n  });\n}\n\ninline constexpr result<void> write_non_finite(writer& out, bool upper_case, bool is_inf) noexcept {\n  if (is_inf) {\n    EMIO_TRYV(out.write_str(upper_case ? \"INF\"sv : \"inf\"sv));\n  } else {\n    EMIO_TRYV(out.write_str(upper_case ? \"NAN\"sv : \"nan\"sv));\n  }\n  return success;\n}\n\n// A floating-point presentation format.\nenum class fp_format : uint8_t {\n  general,  // General: exponent notation or fixed point based on magnitude.\n  exp,      // Exponent notation with the default precision of 6, e.g. 1.2e-3.\n  fixed,    // Fixed point with the default precision of 6, e.g. 0.0012.\n  hex\n};\n\nstruct fp_format_specs {\n  int16_t precision;\n  fp_format format;\n  bool upper_case;\n  bool showpoint;\n};\n\ninline constexpr fp_format_specs parse_fp_format_specs(const format_specs& specs) noexcept {\n  constexpr int16_t default_precision = 6;\n\n  // This spec is typically for general format.\n  fp_format_specs fp_specs{\n      .precision =\n          specs.precision >= 0 || specs.type == no_type ? static_cast<int16_t>(specs.precision) : default_precision,\n      .format = fp_format::general,\n      .upper_case = specs.type == 'E' || specs.type == 'F' || specs.type == 'G',\n      .showpoint = specs.alternate_form,\n  };\n\n  if (specs.type == 'e' || specs.type == 'E') {\n    fp_specs.format = fp_format::exp;\n    fp_specs.precision += 1;\n    fp_specs.showpoint |= specs.precision != 0;\n  } else if (specs.type == 'f' || specs.type == 'F') {\n    fp_specs.format = fp_format::fixed;\n    fp_specs.showpoint |= specs.precision != 0;\n  } else if (specs.type == 'a' || specs.type == 'A') {\n    fp_specs.format = fp_format::hex;\n  }\n  if (fp_specs.format != fp_format::fixed && fp_specs.precision == 0) {\n    fp_specs.precision = 1;  // Calculate at least on significand.\n  }\n  return fp_specs;\n}\n\ninline constexpr char* write_significand(char* out, const char* significand, int significand_size, int integral_size,\n                                         char decimal_point) noexcept {\n  out = copy_n(significand, integral_size, out);\n  if (decimal_point == 0) {\n    return out;\n  }\n  *out++ = decimal_point;\n  return copy_n(significand + integral_size, significand_size - integral_size, out);\n}\n\ninline constexpr char* write_exponent(char* it, int exp) noexcept {\n  if (exp < 0) {\n    *it++ = '-';\n    exp = -exp;\n  } else {\n    *it++ = '+';\n  }\n  int cnt = 2;\n  if (exp >= 100) {\n    write_decimal(to_unsigned(exp), it + 3);\n    return it;\n  } else if (exp < 10) {\n    *it++ = '0';\n    cnt -= 1;\n  }\n  write_decimal(to_unsigned(exp), it + cnt);\n  return it;\n}\n\ninline constexpr result<void> write_decimal(writer& out, format_specs& specs, fp_format_specs& fp_specs,\n                                            bool is_negative, const format_fp_result_t& f) noexcept {\n  const char* significand = f.digits.data();\n  int significand_size = static_cast<int>(f.digits.size());\n  const int output_exp = f.exp - 1;  // 0.1234 x 10^exp => 1.234 x 10^(exp-1)\n  const int abs_output_exp = static_cast<uint16_t>(output_exp >= 0 ? output_exp : -output_exp);\n  const bool has_sign = is_negative || specs.sign == ' ' || specs.sign == '+';\n\n  if (fp_specs.format == fp_format::general && significand_size > 1) {\n    // Remove trailing zeros.\n    auto it = std::find_if(f.digits.rbegin(), f.digits.rend(), [](char c) {\n      return c != '0';\n    });\n    significand_size -= static_cast<int>(it - f.digits.rbegin());\n  }\n\n  const auto use_exp_format = [=]() noexcept {\n    if (fp_specs.format == fp_format::exp) {\n      return true;\n    }\n    if (fp_specs.format != fp_format::general) {\n      return false;\n    }\n    // Use the fixed notation if the exponent is in [exp_lower, exp_upper),\n    // e.g. 0.0001 instead of 1e-04. Otherwise, use the exponent notation.\n    constexpr int exp_lower = -4;\n    constexpr int exp_upper = 16;\n    return output_exp < exp_lower || output_exp >= (fp_specs.precision > 0 ? fp_specs.precision : exp_upper);\n  };\n\n  EMIO_TRY(const char sign_to_write, try_write_sign(out, specs, is_negative));\n\n  int num_zeros = 0;\n  char decimal_point = '.';\n  size_t total_width = static_cast<uint32_t>(has_sign);\n  size_t num_digits = to_unsigned(significand_size);\n\n  if (use_exp_format()) {\n    if (fp_specs.showpoint) {                             // Multiple significands or high precision.\n      num_zeros = fp_specs.precision - significand_size;  // Trailing zeros after an zero only.\n      if (num_zeros < 0) {\n        num_zeros = 0;\n      }\n      num_digits += to_unsigned(num_zeros);\n    } else if (significand_size == 1) {  // One significand.\n      decimal_point = 0;\n    }\n    // The else part is general format with significand size less than the exponent.\n\n    const int exp_digits = abs_output_exp >= 100 ? 3 : 2;\n    num_digits += to_unsigned((decimal_point != 0 ? 1 : 0) + 2 /* sign + e */ + exp_digits);\n    total_width += num_digits;\n\n    return write_padded<alignment::right>(out, specs, total_width, [&]() noexcept -> result<void> {\n      const size_t area_size = num_digits + static_cast<size_t>(sign_to_write != no_sign);\n      EMIO_TRY(auto area, out.get_buffer().get_write_area_of(area_size));\n      auto* it = area.data();\n      if (sign_to_write != no_sign) {\n        *it++ = sign_to_write;\n      }\n\n      it = write_significand(it, significand, significand_size, 1, decimal_point);\n      it = fill_n(it, num_zeros, '0');\n      *it++ = fp_specs.upper_case ? 'E' : 'e';\n      write_exponent(it, output_exp);\n\n      return success;\n    });\n  }\n\n  int integral_size = 0;\n  int num_zeros_2 = 0;\n\n  if (output_exp < 0) {                                                   // Only fractional-part.\n    num_digits += 2;                                                      // For zero + Decimal point.\n    num_zeros = abs_output_exp - 1;                                       // Leading zeros after dot.\n    if (specs.alternate_form && fp_specs.format == fp_format::general) {  // ({:#g}, 0.1) -> 0.100000 instead 0.1\n      num_zeros_2 = fp_specs.precision - significand_size;\n    }\n  } else if ((output_exp + 1) >= significand_size) {  // Only integer-part (including zero).\n    integral_size = significand_size;\n    num_zeros = output_exp - significand_size + 1;  // Trailing zeros.\n    if (fp_specs.showpoint) {                       // Significand is zero but fractional requested.\n      if (specs.alternate_form && fp_specs.format == fp_format::general) {  // ({:#.4g}, 1) -> 1.000 instead of 1.\n        num_zeros_2 = fp_specs.precision - significand_size - num_zeros;\n      } else if (num_zeros == 0) {  // ({:f}, 0) or ({:.4f}, 1.23e-06) -> 0.000000 instead of 0\n        num_zeros_2 = fp_specs.precision;\n      }\n      EMIO_Z_DEV_ASSERT(num_zeros >= 0);\n      num_digits += 1;\n    } else {  // Digit without zero\n      decimal_point = 0;\n    }\n  } else {  // Both parts. Trailing zeros are part of significands.\n    integral_size = output_exp + 1;\n    num_digits += 1;                                                      // Decimal point.\n    if (specs.alternate_form && fp_specs.format == fp_format::general) {  // ({:#g}, 1.2) -> 1.20000 instead 1.2\n      num_zeros = fp_specs.precision - significand_size;\n    }\n    if (fp_specs.format == fp_format::fixed && significand_size > integral_size &&\n        significand_size - integral_size < fp_specs.precision) {  // ({:.4}, 0.99999) -> 1.0000 instead of 1.00\n      num_zeros = fp_specs.precision - (significand_size - integral_size);\n    }\n  }\n  if (num_zeros < 0) {\n    num_zeros = 0;\n  }\n  if (num_zeros_2 < 0) {\n    num_zeros_2 = 0;\n  }\n  num_digits += static_cast<size_t>(num_zeros + num_zeros_2);\n  total_width += num_digits;\n\n  return write_padded<alignment::right>(out, specs, total_width, [&]() noexcept -> result<void> {\n    const size_t area_size = num_digits + static_cast<size_t>(sign_to_write != no_sign);\n    EMIO_TRY(auto area, out.get_buffer().get_write_area_of(area_size));\n    auto* it = area.data();\n    if (sign_to_write != no_sign) {\n      *it++ = sign_to_write;\n    }\n\n    if (output_exp < 0) {\n      *it++ = '0';\n      if (decimal_point != 0) {\n        *it++ = decimal_point;\n        it = fill_n(it, num_zeros, '0');  // TODO: simplify fill_n/copy/copy/n + it\n        it = copy_n(significand, significand_size, it);\n        fill_n(it, num_zeros_2, '0');\n      }\n    } else if ((output_exp + 1) >= significand_size) {\n      it = copy_n(significand, integral_size, it);\n      if (num_zeros != 0) {\n        it = fill_n(it, num_zeros, '0');\n      }\n      if (decimal_point != 0) {\n        *it++ = '.';\n        if (num_zeros_2 != 0) {\n          fill_n(it, num_zeros_2, '0');\n        }\n      }\n    } else {\n      it = write_significand(it, significand, significand_size, integral_size, decimal_point);\n      if (num_zeros != 0) {\n        fill_n(it, num_zeros, '0');\n      }\n    }\n\n    return success;\n  });\n}\n\ninline constexpr std::array<char, 1> zero_digit{'0'};\n\ninline constexpr format_fp_result_t format_decimal(buffer& buffer, const fp_format_specs& fp_specs,\n                                                   const decode_result_t& decoded) noexcept {\n  if (decoded.category == category::zero) {\n    return format_fp_result_t{zero_digit, 1};\n  }\n  switch (fp_specs.format) {\n  case fp_format::general:\n    if (fp_specs.precision == no_precision) {\n      return format_shortest(decoded.finite, buffer);\n    }\n    [[fallthrough]];\n  case fp_format::exp:\n    return format_exact(decoded.finite, buffer, format_exact_mode::significand_digits, fp_specs.precision);\n  case fp_format::fixed: {\n    auto res = format_exact(decoded.finite, buffer, format_exact_mode::decimal_point, fp_specs.precision);\n    if (res.digits.empty()) {\n      return format_fp_result_t{zero_digit, 1};\n    }\n    return res;\n  }\n  case fp_format::hex:\n    std::terminate();\n  }\n  EMIO_Z_INTERNAL_UNREACHABLE;\n}\n\ninline constexpr result<void> format_and_write_decimal(writer& out, format_specs& specs,\n                                                       const decode_result_t& decoded) noexcept {\n  fp_format_specs fp_specs = parse_fp_format_specs(specs);\n\n  if (decoded.category == category::infinity || decoded.category == category::nan) {\n    if (specs.zero_flag) {  // Words aren't prefixed with zeros.\n      specs.fill = ' ';\n      specs.zero_flag = false;\n    }\n    EMIO_TRY(const char sign_to_write, try_write_sign(out, specs, decoded.negative));\n\n    const size_t total_length = 3 + static_cast<uint32_t>(sign_to_write != no_sign);\n    return write_padded<alignment::left>(out, specs, total_length, [&]() noexcept -> result<void> {\n      if (sign_to_write != no_sign) {\n        EMIO_TRYV(out.write_char(sign_to_write));\n      }\n      return write_non_finite(out, fp_specs.upper_case, decoded.category == category::infinity);\n    });\n  }\n\n  emio::memory_buffer buf;\n  const format_fp_result_t res = format_decimal(buf, fp_specs, decoded);\n  return write_decimal(out, specs, fp_specs, decoded.negative, res);\n}\n\ntemplate <typename Arg>\n  requires(std::is_floating_point_v<Arg> && sizeof(Arg) <= sizeof(double))\nconstexpr result<void> write_arg(writer& out, format_specs& specs, const Arg& arg) noexcept {\n  return format_and_write_decimal(out, specs, decode(arg));\n}\n\ninline constexpr result<void> write_arg(writer& out, format_specs& specs, std::string_view arg) noexcept {\n  if (specs.type != '?') {\n    if (specs.precision >= 0) {\n      arg = unchecked_substr(arg, 0, static_cast<size_t>(specs.precision));\n    }\n    return write_padded<alignment::left>(out, specs, arg.size(), [&]() noexcept {\n      return out.write_str(arg);\n    });\n  }\n  const size_t escaped_size = detail::count_size_when_escaped(arg);\n  return write_padded<alignment::left>(out, specs, escaped_size + 2U /* quotes */, [&]() noexcept {\n    return detail::write_str_escaped(out.get_buffer(), arg, escaped_size, '\"');\n  });\n}\n\ntemplate <typename Arg>\n  requires(std::is_same_v<Arg, char>)\nconstexpr result<void> write_arg(writer& out, format_specs& specs, const Arg arg) noexcept {\n  // If a type other than None/c is specified, write out as integer instead of char.\n  if (specs.type != no_type && specs.type != 'c' && specs.type != '?') {\n    return write_arg(out, specs, static_cast<uint8_t>(arg));\n  }\n  if (specs.type != '?') {\n    return write_padded<alignment::left>(out, specs, 1, [&]() noexcept {\n      return out.write_char(arg);\n    });\n  }\n  return write_padded<alignment::left>(out, specs, 3, [&]() noexcept {\n    return out.write_char_escaped(arg);\n  });\n}\n\ntemplate <typename T>\ninline constexpr bool is_void_pointer_v =\n    std::is_pointer_v<T> && std::is_same_v<std::remove_cv_t<std::remove_pointer_t<T>>, void>;\n\ntemplate <typename Arg>\n  requires(is_void_pointer_v<Arg> || std::is_null_pointer_v<Arg>)\nconstexpr result<void> write_arg(writer& out, format_specs& specs, Arg arg) noexcept {\n  specs.alternate_form = true;\n  specs.type = 'x';\n  if constexpr (std::is_null_pointer_v<Arg>) {\n    return write_arg(out, specs, uintptr_t{0});\n  } else {\n    return write_arg(out, specs, std::bit_cast<uintptr_t>(arg));\n  }\n}\n\ntemplate <typename Arg>\n  requires(std::is_same_v<Arg, bool>)\nconstexpr result<void> write_arg(writer& out, format_specs& specs, Arg arg) noexcept {\n  // If a type other than None/s is specified, write out as 1/0 instead of true/false.\n  if (specs.type != no_type && specs.type != 's') {\n    return write_arg(out, specs, static_cast<uint8_t>(arg));\n  }\n  if (arg) {\n    return write_padded<alignment::left>(out, specs, 4, [&]() noexcept {\n      return out.write_str(\"true\"sv);\n    });\n  }\n  return write_padded<alignment::left>(out, specs, 5, [&]() noexcept {\n    return out.write_str(\"false\"sv);\n  });\n}\n\n//\n// Checks.\n//\n\n// specs is passed by reference instead as return type to reduce copying of big value (and code bloat)\ninline constexpr result<void> validate_format_specs(reader& format_rdr, format_specs& specs) noexcept {\n  EMIO_TRY(char c, format_rdr.read_char());\n  if (c == '}') {  // Format end.\n    return success;\n  }\n  if (c == '{') {  // No dynamic spec support.\n    return err::invalid_format;\n  }\n\n  {\n    // Parse for alignment specifier.\n    EMIO_TRY(const char c2, format_rdr.peek());\n    if (c2 == '<' || c2 == '^' || c2 == '>') {\n      if (c2 == '<') {\n        specs.align = alignment::left;\n      } else if (c2 == '^') {\n        specs.align = alignment::center;\n      } else {\n        specs.align = alignment::right;\n      }\n      specs.fill = c;\n      format_rdr.pop();\n      EMIO_TRY(c, format_rdr.read_char());\n    } else if (c == '<' || c == '^' || c == '>') {\n      if (c == '<') {\n        specs.align = alignment::left;\n      } else if (c == '^') {\n        specs.align = alignment::center;\n      } else {\n        specs.align = alignment::right;\n      }\n      EMIO_TRY(c, format_rdr.read_char());\n    }\n  }\n  if (c == '+' || c == '-' || c == ' ') {  // Sign.\n    specs.sign = c;\n    EMIO_TRY(c, format_rdr.read_char());\n  }\n  if (c == '#') {  // Alternate form.\n    specs.alternate_form = true;\n    EMIO_TRY(c, format_rdr.read_char());\n  }\n  if (c == '0') {                          // Zero flag.\n    if (specs.align == alignment::none) {  // If fill/align is used, the zero flag is ignored.\n      specs.fill = '0';\n      specs.align = alignment::right;\n      specs.zero_flag = true;\n    }\n    EMIO_TRY(c, format_rdr.read_char());\n  }\n  if (detail::isdigit(c)) {  // Width.\n    format_rdr.unpop();\n    EMIO_TRY(const uint32_t width, format_rdr.parse_int<uint32_t>());\n    if (width > (static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))) {\n      return err::invalid_format;\n    }\n    specs.width = static_cast<int32_t>(width);\n    EMIO_TRY(c, format_rdr.read_char());\n  }\n  if (c == '.') {  // Precision.\n    if (const result<char> next = format_rdr.peek();\n        next && !isdigit(next.assume_value())) {  // Not followed by a digit.\n      return err::invalid_format;\n    }\n    EMIO_TRY(const uint32_t precision, format_rdr.parse_int<uint32_t>());\n    if (precision > (static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))) {\n      return err::invalid_format;\n    }\n    specs.precision = static_cast<int32_t>(precision);\n    EMIO_TRY(c, format_rdr.read_char());\n  }\n  if (detail::isalpha(c) || c == '?') {  // Type.\n    specs.type = c;\n    EMIO_TRY(c, format_rdr.read_char());\n  }\n  if (c == '}') {  // Format end.\n    return success;\n  }\n  return err::invalid_format;\n}\n\ninline constexpr result<void> parse_format_specs(reader& format_rdr, format_specs& specs) noexcept {\n  char c = format_rdr.read_char().assume_value();\n  if (c == '}') {  // Format end.\n    return success;\n  }\n\n  {\n    // Parse for alignment specifier.\n    const char c2 = format_rdr.peek().assume_value();\n    if (c2 == '<' || c2 == '^' || c2 == '>') {\n      if (c2 == '<') {\n        specs.align = alignment::left;\n      } else if (c2 == '^') {\n        specs.align = alignment::center;\n      } else {\n        specs.align = alignment::right;\n      }\n      specs.fill = c;\n      format_rdr.pop();\n      c = format_rdr.read_char().assume_value();\n    } else if (c == '<' || c == '^' || c == '>') {\n      if (c == '<') {\n        specs.align = alignment::left;\n      } else if (c == '^') {\n        specs.align = alignment::center;\n      } else {\n        specs.align = alignment::right;\n      }\n      c = format_rdr.read_char().assume_value();\n    }\n  }\n  if (c == '+' || c == '-' || c == ' ') {  // Sign.\n    specs.sign = c;\n    c = format_rdr.read_char().assume_value();\n  }\n  if (c == '#') {  // Alternate form.\n    specs.alternate_form = true;\n    c = format_rdr.read_char().assume_value();\n  }\n  if (c == '0') {                          // Zero flag.\n    if (specs.align == alignment::none) {  // Ignoreable.\n      specs.fill = '0';\n      specs.align = alignment::right;\n      specs.zero_flag = true;\n    }\n    c = format_rdr.read_char().assume_value();\n  }\n  if (detail::isdigit(c)) {  // Width.\n    format_rdr.unpop();\n    specs.width = static_cast<int32_t>(format_rdr.parse_int<uint32_t>().assume_value());\n    c = format_rdr.read_char().assume_value();\n  }\n  if (c == '.') {  // Precision.\n    specs.precision = static_cast<int32_t>(format_rdr.parse_int<uint32_t>().assume_value());\n    c = format_rdr.read_char().assume_value();\n  }\n  if (detail::isalpha(c) || c == '?') {  // Type.\n    specs.type = c;\n    format_rdr.pop();  // format_rdr.read_char() in validate_format_specs;\n  }\n  return success;\n}\n\ninline constexpr result<void> check_integral_specs(const format_specs& specs) noexcept {\n  if (specs.precision != no_precision) {\n    return err::invalid_format;\n  }\n  switch (specs.type) {\n  case no_type:\n  case 'd':\n  case 'x':\n  case 'X':\n  case 'b':\n  case 'B':\n  case 'c':\n  case 'o':\n  case 'O':\n    return success;\n  default:\n    return err::invalid_format;\n  }\n}\n\ninline constexpr result<void> check_unsigned_specs(const format_specs& specs) noexcept {\n  if (specs.sign == no_sign) {\n    return success;\n  }\n  return err::invalid_format;\n}\n\ninline constexpr result<void> check_bool_specs(const format_specs& specs) noexcept {\n  if (specs.type != no_type && specs.type != 's') {\n    return check_integral_specs(specs);\n  }\n  if (specs.precision != no_precision) {\n    return err::invalid_format;\n  }\n  return success;\n}\n\ninline constexpr result<void> check_char_specs(const format_specs& specs) noexcept {\n  if (specs.type != no_type && specs.type != 'c' && specs.type != '?') {\n    return check_integral_specs(specs);\n  }\n  if (specs.alternate_form || specs.sign != no_sign || specs.zero_flag || specs.precision != no_precision) {\n    return err::invalid_format;\n  }\n  return success;\n}\n\ninline constexpr result<void> check_pointer_specs(const format_specs& specs) noexcept {\n  if (specs.type != no_type && specs.type != 'p') {\n    return err::invalid_format;\n  }\n  if (specs.alternate_form || specs.sign != no_sign || specs.zero_flag || specs.precision != no_precision) {\n    return err::invalid_format;\n  }\n  return success;\n}\n\ninline constexpr result<void> check_floating_point_specs(const format_specs& specs) noexcept {\n  if (specs.precision > 1100) {\n    return err::invalid_format;\n  }\n\n  switch (specs.type) {\n  case no_type:\n  case 'f':\n  case 'F':\n  case 'e':\n  case 'E':\n  case 'g':\n  case 'G':\n    //  case 'a': Not supported yet.\n    //  case 'A':\n    return success;\n  default:\n    return err::invalid_format;\n  }\n}\n\ninline constexpr result<void> check_string_specs(const format_specs& specs) noexcept {\n  if (specs.alternate_form || specs.sign != no_sign || specs.zero_flag ||\n      (specs.precision != no_precision && specs.type == '?') ||\n      (specs.type != no_type && specs.type != 's' && specs.type != '?')) {\n    return err::invalid_format;\n  }\n  return success;\n}\n\n//\n// Type traits.\n//\n\n// Specifies if T has an enabled formatter specialization.\ntemplate <typename Arg>\ninline constexpr bool has_formatter_v = std::is_constructible_v<formatter<Arg>>;\n\ntemplate <typename Arg>\ninline constexpr bool format_can_fail_v = false;\n\ntemplate <typename Arg>\n  requires requires(Arg) { formatter<Arg>::format_can_fail; }\ninline constexpr bool format_can_fail_v<Arg> = formatter<Arg>::format_can_fail;\n\ntemplate <typename T>\nconcept has_validate_function_v = requires {\n  { formatter<T>::validate(std::declval<reader&>()) } -> std::same_as<result<void>>;\n};\n\ntemplate <typename T>\nconcept has_static_validate_function_v = requires { &formatter<T>::validate; };\n\ntemplate <typename T>\nconcept has_member_validate_function_v = requires { std::declval<formatter<T>>().validate(std::declval<reader&>()); };\n\ntemplate <typename T>\nconcept has_any_validate_function_v =\n    has_static_validate_function_v<T> || std::is_member_function_pointer_v<decltype(&formatter<T>::validate)> ||\n    has_member_validate_function_v<T>;\n\ntemplate <typename Arg>\nconstexpr result<void> validate_trait(reader& format_rdr) {\n  // Check if a formatter exist and a correct validate method is implemented. If not, use the parse method.\n  if constexpr (has_formatter_v<Arg>) {\n    if constexpr (has_validate_function_v<Arg>) {\n      return formatter<Arg>::validate(format_rdr);\n    } else {\n      static_assert(!has_any_validate_function_v<Arg>,\n                    \"Formatter seems to have a validate property which doesn't fit the desired signature.\");\n      return formatter<Arg>{}.parse(format_rdr);\n    }\n  } else {\n    static_assert(has_formatter_v<Arg>,\n                  \"Cannot format an argument. To make type T formattable provide a formatter<T> specialization.\");\n    return err::invalid_format;\n  }\n}\n\ntemplate <typename T>\ninline constexpr bool is_core_type_v =\n    std::is_same_v<T, bool> || std::is_same_v<T, char> || std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t> ||\n    std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> || std::is_same_v<T, double> ||\n    std::is_null_pointer_v<T> || is_void_pointer_v<T> || std::is_same_v<T, std::string_view>;\n\ntemplate <typename T>\nconcept has_format_as = requires(T arg) { format_as(arg); };\n\ntemplate <typename T>\nusing format_as_return_t = decltype(format_as(std::declval<T>()));\n\n// To reduce code bloat, similar types are unified to a general one.\ntemplate <typename T>\nstruct unified_type;\n\ntemplate <typename T>\nstruct unified_type {\n  using type = const T&;\n};\n\ntemplate <typename T>\n  requires(!std::is_integral_v<T> && !std::is_null_pointer_v<T> && std::is_convertible_v<T, std::string_view>)\nstruct unified_type<T> {\n  using type = std::string_view;\n};\n\ntemplate <typename T>\n  requires(std::is_integral_v<T> && std::is_signed_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<T, char>)\nstruct unified_type<T> {\n  using type = std::conditional_t<num_bits<T>() <= 32, int32_t, int64_t>;\n};\n\ntemplate <typename T>\n  requires(std::is_floating_point_v<T> && sizeof(T) <= sizeof(double))\nstruct unified_type<T> {\n  using type = double;\n};\n\ntemplate <typename T>\n  requires(std::is_same_v<T, char> || std::is_same_v<T, bool> || is_void_pointer_v<T> || std::is_null_pointer_v<T>)\nstruct unified_type<T> {\n  using type = T;\n};\n\ntemplate <typename T>\n  requires(std::is_integral_v<T> && std::is_unsigned_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<T, char>)\nstruct unified_type<T> {\n  using type = std::conditional_t<num_bits<T>() <= 32, uint32_t, uint64_t>;\n};\n\ntemplate <typename T>\nusing unified_type_t = typename unified_type<T>::type;\n\n}  // namespace detail::format\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/detail/format/parser.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"args.hpp\"\n#include \"formatter.hpp\"\n\nnamespace emio::detail::format {\n\nclass format_parser final : public parser<format_parser, input_validation::disabled> {\n public:\n  constexpr explicit format_parser(writer& out, reader& format_rdr) noexcept\n      : parser<format_parser, input_validation::disabled>{format_rdr}, out_{out} {}\n\n  format_parser(const format_parser&) = delete;\n  format_parser(format_parser&&) = delete;\n  format_parser& operator=(const format_parser&) = delete;\n  format_parser& operator=(format_parser&&) = delete;\n  constexpr ~format_parser() noexcept override;  // NOLINT(performance-trivially-destructible): See definition.\n\n  constexpr result<void> process(const std::string_view& str) noexcept override {\n    return out_.write_str(str);\n  }\n\n  result<void> process_arg(const format_arg& arg) noexcept {\n    return arg.process_arg(out_, format_rdr_);\n  }\n\n  template <typename Arg>\n  constexpr result<void> process_arg(const Arg& arg) noexcept {\n    if constexpr (has_formatter_v<Arg>) {\n      formatter<Arg> formatter;\n      EMIO_TRYV(formatter.parse(this->format_rdr_));\n      return formatter.format(out_, arg);\n    } else {\n      static_assert(has_formatter_v<Arg>,\n                    \"Cannot format an argument. To make type T formattable provide a formatter<T> specialization.\");\n    }\n  }\n\n private:\n  writer& out_;\n};\n\n// Explicit out-of-class definition because of GCC bug: <destructor> used before its definition.\nconstexpr format_parser::~format_parser() noexcept = default;\n\nclass format_specs_checker final : public parser<format_specs_checker, input_validation::enabled> {\n public:\n  using parser<format_specs_checker, input_validation::enabled>::parser;\n\n  format_specs_checker(const format_specs_checker& other) = delete;\n  format_specs_checker(format_specs_checker&& other) = delete;\n  format_specs_checker& operator=(const format_specs_checker& other) = delete;\n  format_specs_checker& operator=(format_specs_checker&& other) = delete;\n  constexpr ~format_specs_checker() noexcept override;  // NOLINT(performance-trivially-destructible): See definition.\n\n  result<void> process_arg(const format_validation_arg& arg) noexcept {\n    return arg.validate(this->format_rdr_);\n  }\n\n  template <typename Arg>\n  constexpr result<void> process_arg(std::type_identity<Arg> /*unused*/) noexcept {\n    return format_arg_trait<std::remove_cvref_t<Arg>>::validate(this->format_rdr_);\n  }\n};\n\n// Explicit out-of-class definition because of GCC bug: <destructor> used before its definition.\nconstexpr format_specs_checker::~format_specs_checker() noexcept = default;\n\n}  // namespace emio::detail::format\n"
  },
  {
    "path": "include/emio/detail/format/ranges.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <span>\n#include <string_view>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"../../formatter.hpp\"\n\nnamespace emio::detail::format {\n\n// For range.\n\nusing std::begin;\nusing std::data;\nusing std::end;\nusing std::size;\n\ntemplate <typename T>\nconcept advanceable = requires(T x) { ++x; };\n\ntemplate <typename T>\nconcept is_iterable = std::is_array_v<T> || requires(T x) {\n  { begin(x) } -> advanceable;\n  requires !std::is_same_v<decltype(*begin(x)), void>;\n  { static_cast<bool>(begin(x) != end(x)) };\n};\n\ntemplate <typename T>\nusing element_type_t = std::remove_cvref_t<decltype(*begin(std::declval<std::remove_reference_t<T>&>()))>;\n\ntemplate <typename T>\nconcept is_map = requires(T x) { typename T::mapped_type; };\n\ntemplate <typename T>\nconcept is_set = requires(T x) { typename T::key_type; };\n\ntemplate <typename T>\nconcept is_string_like = !std::is_null_pointer_v<T> && std::is_convertible_v<T, std::string_view>;\n\ntemplate <typename T>\nconcept is_valid_range = is_iterable<T> && !is_string_like<T> && is_formattable_v<element_type_t<T>>;\n\ntemplate <typename T>\nstruct is_span : std::false_type {};\n\ntemplate <typename T, size_t N>\nstruct is_span<std::span<T, N>> : std::true_type {};\n\ntemplate <typename T>\nconcept is_contiguous_but_not_span = std::is_array_v<T> || requires(T x) {\n  requires !is_span<T>::value;\n  requires std::is_same_v<std::remove_cvref_t<decltype(*data(x))>, element_type_t<T>>;\n  { size(x) } -> std::same_as<size_t>;\n};\n\nstruct ranges_specs {\n  std::string_view opening_bracket{};\n  std::string_view closing_bracket{};\n  std::string_view separator{};\n};\n\ntemplate <typename Formatter>\n  requires requires(Formatter f) { f.set_debug_format(true); }\nconstexpr void maybe_set_debug_format(Formatter& f, bool set) noexcept {\n  f.set_debug_format(set);\n}\n\ntemplate <typename Formatter>\nconstexpr void maybe_set_debug_format(Formatter& /*unused*/, ...) noexcept {}\n\n// For tuple like types.\n\nusing std::get;\n\n// From https://stackoverflow.com/a/68444475/1611317\ntemplate <class T, std::size_t N>\nconcept has_tuple_element = requires(T t) {\n  typename std::tuple_element_t<N, std::remove_const_t<T>>;\n  { get<N>(t) } -> std::convertible_to<const std::tuple_element_t<N, T>&>;\n};\n\ntemplate <typename T, size_t... Ns>\nconstexpr auto has_tuple_element_unpack(std::index_sequence<Ns...> /*unused*/) noexcept {\n  return (has_tuple_element<T, Ns> && ...);\n}\n\ntemplate <class T>\nconcept is_tuple_like = !std::is_reference_v<T> && requires(T t) {\n  typename std::tuple_size<T>::type;\n  requires std::derived_from<std::tuple_size<T>, std::integral_constant<std::size_t, std::tuple_size_v<T>>>;\n} && has_tuple_element_unpack<T>(std::make_index_sequence<std::tuple_size_v<T>>());\n\ntemplate <typename T, size_t... Ns>\nconstexpr auto is_formattable_unpack(std::index_sequence<Ns...> /*unused*/) noexcept {\n  return (is_formattable_v<decltype(get<Ns>(std::declval<T&>()))> && ...);\n}\n\ntemplate <typename T>\nconcept is_valid_tuple = !is_valid_range<T> && is_tuple_like<T> &&\n                         is_formattable_unpack<T>(std::make_index_sequence<std::tuple_size_v<T>>());\n\ntemplate <typename T, std::size_t... Ns>\nauto get_tuple_formatters(std::index_sequence<Ns...> /*unused*/)\n    -> std::tuple<formatter<std::remove_cvref_t<std::tuple_element_t<Ns, T>>>...>;\n\ntemplate <typename T>\nusing tuple_formatters = decltype(get_tuple_formatters<T>(std::make_index_sequence<std::tuple_size_v<T>>{}));\n\n}  // namespace emio::detail::format\n"
  },
  {
    "path": "include/emio/detail/format/specs.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <cstdint>\n\nnamespace emio::detail::format {\n\n// \"{\" [arg_id] [\":\" (format_spec)]\n\n// replacement_field ::=  \"{\" [arg_id] [\":\" (format_spec)] \"}\"\n// arg_id            ::=  integer\n// integer           ::=  digit+\n// digit             ::=  \"0\"...\"9\"\n\n// format_spec ::=  [[fill]align][sign][\"#\"][\"0\"][width][\".\" precision][\"L\"][type]\n// fill        ::=  <a character other than '{' or '}'>\n// align       ::=  \"<\" | \">\" | \"^\"\n// sign        ::=  \"+\" | \"-\" | \" \"\n// width       ::=  integer (<=int max)\n// precision   ::=  integer (<=int max)\n// type        ::=  \"a\" | \"A\" | \"b\" | \"B\" | \"c\" | \"d\" | \"e\" | \"E\" | \"f\" | \"F\" | \"g\" | \"G\"| \"o\" | \"O\" | \"p\" | \"s\" | \"x\"\n//                  | \"X\"\n\ninline constexpr char no_sign = '\\0';\ninline constexpr int no_precision = -1;\ninline constexpr char no_type = 0;\n\nenum class alignment : uint8_t { none, left, center, right };\n\nstruct format_specs {\n  char fill{' '};\n  alignment align{alignment::none};\n  char sign{no_sign};\n  bool alternate_form{false};\n  bool zero_flag{false};\n  int32_t width{0};\n  int32_t precision{no_precision};\n  char type{no_type};\n};\n\n}  // namespace emio::detail::format\n"
  },
  {
    "path": "include/emio/detail/misc.hpp",
    "content": "//\n// Copyright (c) 20213- present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <type_traits>\n\nnamespace emio::detail {\n\ntemplate <typename T>\nstruct always_false : std::false_type {};\n\ntemplate <typename T>\ninline constexpr bool always_false_v = always_false<T>::value;\n\ntemplate <typename... Ts>\nstruct overload : Ts... {\n  using Ts::operator()...;\n};\n\ntemplate <class... Ts>\noverload(Ts...) -> overload<Ts...>;\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/detail/parser.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <cstdint>\n#include <limits>\n#include <optional>\n#include <string_view>\n\n#include \"../reader.hpp\"\n#include \"args.hpp\"\n#include \"bitset.hpp\"\n\nnamespace emio::detail {\n\n/**\n * Flag to enable/disable input validation.\n */\nenum class input_validation { enabled, disabled };\n\ninline constexpr uint8_t no_more_args = std::numeric_limits<uint8_t>::max();\n\ntemplate <input_validation>\nclass parser_base {\n public:\n  constexpr explicit parser_base(reader& format_rdr) noexcept : format_rdr_{format_rdr} {}\n\n  parser_base(const parser_base&) = delete;\n  parser_base(parser_base&&) = delete;\n  parser_base& operator=(const parser_base&) = delete;\n  parser_base& operator=(parser_base&&) = delete;\n\n  virtual constexpr ~parser_base() = default;\n\n  constexpr result<void> parse(uint8_t& arg_nbr) noexcept {\n    const char*& it = get_it(format_rdr_);\n    const char* const end = get_end(format_rdr_);\n    while (it != end) {\n      const char c = *it++;\n      if (c == '{') {\n        if (it == end) {\n          return emio::err::invalid_format;\n        }\n        if (*it == '{') {\n          ++it;\n        } else {\n          return parse_replacement_field(arg_nbr);\n        }\n      } else if (c == '}') {\n        if (it == end || *it != '}') {\n          return err::invalid_format;\n        }\n        ++it;\n      }\n    }\n    return success;\n  }\n\n protected:\n  reader& format_rdr_;\n\n private:\n  constexpr result<void> parse_replacement_field(uint8_t& arg_nbr) noexcept {\n    EMIO_TRYV(parse_field_name(arg_nbr));\n\n    EMIO_TRY(const char c, format_rdr_.peek());\n    if (c == '}') {\n      return success;\n    }\n    if (c == ':') {  // Format specs.\n      // Format specs are parsed with known argument type later.\n      format_rdr_.pop();\n      return success;\n    }\n    return err::invalid_format;\n  }\n\n  constexpr result<void> parse_field_name(uint8_t& arg_nbr) noexcept {\n    EMIO_TRY(const char c, format_rdr_.peek());\n    if (detail::isdigit(c)) {               // Positional argument.\n      if (use_positional_args_ == false) {  // If first argument was positional -> failure.\n        return err::invalid_format;\n      }\n      EMIO_TRY(arg_nbr, format_rdr_.template parse_int<uint8_t>());\n      use_positional_args_ = true;\n    } else {\n      if (use_positional_args_ == true) {\n        return err::invalid_format;\n      }\n      use_positional_args_ = false;\n      // None positional argument. Increase arg_nbr after each format specifier.\n      arg_nbr = increment_arg_number_++;\n    }\n    return success;\n  }\n\n  std::optional<bool> use_positional_args_{};\n  uint8_t increment_arg_number_{};\n};\n\ntemplate <>\nclass parser_base<input_validation::disabled> {\n public:\n  constexpr explicit parser_base(reader& format_rdr) noexcept : format_rdr_{format_rdr} {}\n\n  parser_base(const parser_base& other) = delete;\n  parser_base(parser_base&& other) = delete;\n  parser_base& operator=(const parser_base& other) = delete;\n  parser_base& operator=(parser_base&& other) = delete;\n\n  virtual constexpr ~parser_base() = default;\n\n  constexpr result<void> parse(uint8_t& arg_nbr) noexcept {\n    const char*& it = get_it(format_rdr_);\n    const char* const end = get_end(format_rdr_);\n    const char* begin = it;\n    while (it != end) {\n      const char c = *it++;\n      if (c == '{') {\n        EMIO_Z_DEV_ASSERT(it != end);\n        if (*it == '{') {\n          if (begin != it) {\n            EMIO_TRYV(process(std::string_view{begin, it}));\n            begin = ++it;\n          }\n        } else {\n          if (begin != (it - 1)) {\n            EMIO_TRYV(process(std::string_view{begin, it - 1}));\n          }\n          return parse_replacement_field(arg_nbr);\n        }\n      } else if (c == '}') {\n        EMIO_Z_DEV_ASSERT(it != end);\n        if (begin != it) {\n          EMIO_TRYV(process(std::string_view{begin, it}));\n          begin = ++it;\n        }\n      }\n    }\n    if (begin != it) {\n      EMIO_TRYV(process(std::string_view{begin, it}));\n    }\n    return success;\n  }\n\n protected:\n  virtual constexpr result<void> process(const std::string_view& str) noexcept = 0;\n\n  reader& format_rdr_;\n\n private:\n  constexpr result<void> parse_replacement_field(uint8_t& arg_nbr) noexcept {\n    parse_field_name(arg_nbr);\n    const char c = format_rdr_.peek().assume_value();\n    if (c == '}') {\n      return success;\n    }\n    format_rdr_.pop();\n    return success;\n  }\n\n  constexpr void parse_field_name(uint8_t& arg_nbr) noexcept {\n    const char c = format_rdr_.peek().assume_value();\n    if (detail::isdigit(c)) {  // Positional argument.\n      arg_nbr = format_rdr_.template parse_int<uint8_t>().assume_value();\n      use_positional_args_ = true;\n    } else {\n      use_positional_args_ = false;\n      // None positional argument. Increase arg_nbr after each format specifier.\n      arg_nbr = increment_arg_number_++;\n    }\n  }\n\n  std::optional<bool> use_positional_args_{};\n  uint8_t increment_arg_number_{};\n};\n\n// Wrapper around an args_span of the string to be parsed so that it can be distinguished from a normal argument.\ntemplate <typename Arg>\nstruct related_format_args {\n  const args_span<Arg>& args;\n};\n\ntemplate <typename Arg>\nrelated_format_args(const args_span<Arg>&) -> related_format_args<Arg>;\n\ntemplate <typename CRTP, input_validation Validation>\nclass parser : public parser_base<Validation> {\n public:\n  using parser_base<Validation>::parser_base;\n\n  parser(const parser&) = delete;\n  parser(parser&&) = delete;\n  parser& operator=(const parser&) = delete;\n  parser& operator=(parser&&) = delete;\n  constexpr ~parser() noexcept override;  // NOLINT(performance-trivially-destructible): See definition.\n\n  template <typename Arg>\n  result<void> apply(uint8_t arg_nbr, const related_format_args<Arg>& args) noexcept {\n    return static_cast<CRTP*>(this)->process_arg(args.args.get_args()[arg_nbr]);\n  }\n\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static): not possible because of template function\n  constexpr result<void> apply(uint8_t /*arg_pos*/) noexcept {\n    return err::invalid_format;\n  }\n\n  template <typename Arg, typename... Args>\n  constexpr result<void> apply(uint8_t arg_pos, Arg& arg, Args&... args) noexcept {\n    if (arg_pos == 0) {\n      return static_cast<CRTP*>(this)->process_arg(arg);\n    }\n    return apply(arg_pos - 1, args...);\n  }\n};\n\n// Explicit out-of-class definition because of GCC bug: <destructor> used before its definition.\ntemplate <typename CRTP, input_validation Validation>\nconstexpr parser<CRTP, Validation>::~parser() noexcept = default;\n\ntemplate <typename Parser, typename... Args>\nconstexpr bool validate(std::string_view str, const size_t arg_cnt, const Args&... args) noexcept {\n  reader format_rdr{str};\n  Parser parser{format_rdr};\n  bitset<128> matched{};\n  while (true) {\n    uint8_t arg_nbr{detail::no_more_args};\n    if (auto res = parser.parse(arg_nbr); !res) {\n      return false;\n    }\n    if (arg_nbr == detail::no_more_args) {\n      break;\n    }\n    if (arg_cnt <= arg_nbr) {\n      return false;\n    }\n    matched.set(arg_nbr);\n    auto res = parser.apply(arg_nbr, args...);\n    if (!res) {\n      return false;\n    }\n  }\n  return matched.all_first(arg_cnt);\n}\n\ntemplate <typename Parser, typename T, typename... Args>\nconstexpr result<void> parse(std::string_view str, T& input, Args&&... args) noexcept {\n  reader format_rdr{str};\n  Parser parser{input, format_rdr};\n  while (true) {\n    uint8_t arg_nbr{detail::no_more_args};\n    if (auto res = parser.parse(arg_nbr); !res) {\n      return res.assume_error();\n    }\n    if (arg_nbr == detail::no_more_args) {\n      break;\n    }\n    if (auto res = parser.apply(arg_nbr, std::forward<Args>(args)...); !res) {\n      return res.assume_error();\n    }\n  }\n  return success;\n}\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/detail/predef.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\nnamespace emio::detail {\n\n// Helper macros for removing parentheses.\n#define EMIO_Z_INTERNAL_DEPAREN(X) EMIO_Z_INTERNAL_ESC(EMIO_Z_INTERNAL_ISH_EMIO_INTERNAL X)\n#define EMIO_Z_INTERNAL_ISH_EMIO_INTERNAL(...) EMIO_Z_INTERNAL_ISH_EMIO_INTERNAL __VA_ARGS__\n#define EMIO_Z_INTERNAL_ESC(...) EMIO_Z_INTERNAL_ESC_(__VA_ARGS__)\n#define EMIO_Z_INTERNAL_ESC_(...) EMIO_Z_INTERNAL_VAN##__VA_ARGS__\n#define EMIO_Z_INTERNAL_VANEMIO_Z_INTERNAL_ISH_EMIO_INTERNAL\n\n// Helper macros for generating an unique name.\n#define EMIO_Z_INTERNAL_GLUE2(x, y) x##y\n#define EMIO_Z_INTERNAL_GLUE(x, y) EMIO_Z_INTERNAL_GLUE2(x, y)\n#define EMIO_Z_INTERNAL_UNIQUE_NAME EMIO_Z_INTERNAL_GLUE(_emio_try_unique_name_temporary, __COUNTER__)\n\n#define EMIO_Z_INTERNAL_TRYV(name, expr)                     \\\n  do {                                                       \\\n    if (auto name = (expr); name.has_error()) [[unlikely]] { \\\n      return name.assume_error();                            \\\n    }                                                        \\\n  } while (0)\n\n#define EMIO_Z_INTERNAL_TRY(name, var, expr) \\\n  auto name = (expr);                        \\\n  if (name.has_error()) [[unlikely]] {       \\\n    return name.assume_error();              \\\n  }                                          \\\n  EMIO_Z_INTERNAL_DEPAREN(var) = std::move(name).assume_value()\n\n#if defined(__GNUC__) || defined(__GNUG__)\n// Separate macro instead of std::is_constant_evaluated() because code will be optimized away even in debug if inlined.\n#  define EMIO_Z_INTERNAL_IS_CONST_EVAL __builtin_is_constant_evaluated()\n#  define EMIO_Z_INTERNAL_UNREACHABLE __builtin_unreachable()\n#else\n#  define EMIO_Z_INTERNAL_IS_CONST_EVAL std::is_constant_evaluated()\n#  define EMIO_Z_INTERNAL_UNREACHABLE std::terminate()\n#endif\n\n#if defined(EMIO_ENABLE_DEV_ASSERT)\n#  define EMIO_Z_DEV_ASSERT(...) \\\n    do {                         \\\n      if (!(__VA_ARGS__)) {      \\\n        std::terminate();        \\\n      }                          \\\n    } while (0)\n#else\n#  define EMIO_Z_DEV_ASSERT(...) static_cast<void>(__VA_ARGS__)\n#endif\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/detail/scan/args.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"../../scanner.hpp\"\n#include \"../args.hpp\"\n\nnamespace emio::detail::scan {\n\ntemplate <typename Arg>\nstruct scan_arg_trait {\n  using unified_type = Arg&;\n\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    // Check if a scanner exist and a correct validate method is implemented. If not, use the parse method.\n    if constexpr (has_scanner_v<Arg>) {\n      if constexpr (has_validate_function_v<Arg>) {\n        return scanner<Arg>::validate(format_rdr);\n      } else {\n        static_assert(!has_any_validate_function_v<Arg>,\n                      \"Scanner seems to have a validate property which doesn't fit the desired signature.\");\n        return scanner<Arg>{}.parse(format_rdr);\n      }\n    } else {\n      static_assert(has_scanner_v<Arg>,\n                    \"Cannot scan an argument. To make type T scannable provide a scanner<T> specialization.\");\n      return err::invalid_format;\n    }\n  }\n\n  static constexpr result<void> process_arg(reader& in, reader& format_rdr, Arg& arg) noexcept {\n    scanner<Arg> scanner;\n    EMIO_TRYV(scanner.parse(format_rdr));\n    return scanner.scan(in, arg);\n  }\n};\n\nusing scan_validation_arg = validation_arg<scan_arg_trait>;\n\nusing scan_arg = arg<reader, scan_arg_trait>;\n\nusing scan_args = args_span_with_str<scan_arg>;\n\n}  // namespace emio::detail::scan\n"
  },
  {
    "path": "include/emio/detail/scan/parser.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"args.hpp\"\n#include \"scanner.hpp\"\n\nnamespace emio::detail::scan {\n\nclass scan_parser final : public parser<scan_parser, input_validation::disabled> {\n public:\n  constexpr explicit scan_parser(reader& in, reader& format_rdr) noexcept\n      : parser<scan_parser, input_validation::disabled>{format_rdr}, in_{in} {}\n\n  scan_parser(const scan_parser&) = delete;\n  scan_parser(scan_parser&&) = delete;\n  scan_parser& operator=(const scan_parser&) = delete;\n  scan_parser& operator=(scan_parser&&) = delete;\n  constexpr ~scan_parser() noexcept override;  // NOLINT(performance-trivially-destructible): See definition.\n\n  constexpr result<void> process(const std::string_view& str) noexcept override {\n    return in_.read_if_match_str(str);\n  }\n\n  result<void> process_arg(const scan_arg& arg) noexcept {\n    return arg.process_arg(in_, format_rdr_);\n  }\n\n  template <typename Arg>\n  constexpr result<void> process_arg(Arg& arg) noexcept {\n    if constexpr (has_scanner_v<Arg>) {\n      scanner<Arg> scanner;\n      EMIO_TRYV(scanner.parse(this->format_rdr_));\n      return scanner.scan(in_, arg);\n    } else {\n      static_assert(has_scanner_v<Arg>,\n                    \"Cannot scan an argument. To make type T scannable provide a scanner<T> specialization.\");\n    }\n  }\n\n private:\n  reader& in_;\n};\n\n// Explicit out-of-class definition because of GCC bug: <destructor> used before its definition.\nconstexpr scan_parser::~scan_parser() noexcept = default;\n\nclass scan_specs_checker final : public parser<scan_specs_checker, input_validation::enabled> {\n public:\n  using parser<scan_specs_checker, input_validation::enabled>::parser;\n\n  scan_specs_checker(const scan_specs_checker& other) = delete;\n  scan_specs_checker(scan_specs_checker&& other) = delete;\n  scan_specs_checker& operator=(const scan_specs_checker& other) = delete;\n  scan_specs_checker& operator=(scan_specs_checker&& other) = delete;\n  constexpr ~scan_specs_checker() noexcept override;  // NOLINT(performance-trivially-destructible): See definition.\n\n  result<void> process_arg(const scan_validation_arg& arg) noexcept {\n    return arg.validate(this->format_rdr_);\n  }\n\n  template <typename Arg>\n  constexpr result<void> process_arg(std::type_identity<Arg> /*unused*/) noexcept {\n    return scan_arg_trait<std::remove_cvref_t<Arg>>::validate(this->format_rdr_);\n  }\n};\n\n// Explicit out-of-class definition because of GCC bug: <destructor> used before its definition.\nconstexpr scan_specs_checker::~scan_specs_checker() noexcept = default;\n\n}  // namespace emio::detail::scan\n"
  },
  {
    "path": "include/emio/detail/scan/scan_from.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"../../reader.hpp\"\n#include \"../../writer.hpp\"\n#include \"../validated_string.hpp\"\n#include \"args.hpp\"\n#include \"parser.hpp\"\n\nnamespace emio::detail::scan {\n\nstruct scan_trait {\n  template <typename... Args>\n  [[nodiscard]] static constexpr bool validate_string(std::string_view format_str) noexcept {\n    if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n      return validate<scan_specs_checker>(format_str, sizeof...(Args), std::type_identity<Args>{}...);\n    } else {\n      return validate<scan_specs_checker>(format_str, sizeof...(Args),\n                                          related_format_args{make_validation_args<scan_validation_arg, Args...>()});\n    }\n  }\n};\n\ntemplate <typename... Args>\nusing format_string = validated_string<scan_trait, std::type_identity_t<Args>...>;\n\ntemplate <typename... Args>\nusing valid_format_string = valid_string<scan_trait, std::type_identity_t<Args>...>;\n\ninline result<void> vscan_from(reader& in, const scan_args& args) noexcept {\n  EMIO_TRY(const std::string_view str, args.get_str());\n  if (args.is_plain_str()) {\n    return in.read_if_match_str(str);\n  }\n  return parse<scan_parser>(str, in, related_format_args{args});\n}\n\ntemplate <typename... Args>\nconstexpr result<void> scan_from(reader& in, const format_string<Args...>& format_str, Args&... args) noexcept {\n  EMIO_TRY(const std::string_view str, format_str.get());\n  if (format_str.is_plain_str()) {\n    return in.read_if_match_str(str);\n  }\n  return parse<scan_parser>(str, in, args...);\n}\n\n}  // namespace emio::detail::scan\n"
  },
  {
    "path": "include/emio/detail/scan/scanner.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"../../reader.hpp\"\n#include \"specs.hpp\"\n\nnamespace emio {\n\ntemplate <typename>\nclass scanner;\n\nnamespace detail::scan {\n\n//\n// Read args.\n//\n\ninline constexpr result<void> disallow_sign(reader& in) noexcept {\n  EMIO_TRY(const char c, in.peek());\n  if (c == '+' || c == '-') {\n    return err::invalid_data;\n  }\n  return success;\n}\n\ninline constexpr result<int> determine_base(reader in) noexcept {\n  EMIO_TRY(char c, in.read_char());\n  if (c != '0') {\n    // Assume decimal.\n    return 10;\n  }\n  result<char> next_char = in.read_char();\n  if (!next_char) {\n    // Assume decimal.\n    return 10;\n  }\n  c = next_char.assume_value();\n  if (c == 'b' || c == 'B') {\n    return 2;\n  }\n  if (c == 'x' || c == 'X') {\n    return 16;\n  }\n  return 8;\n}\n\ninline constexpr int get_base(char type) noexcept {\n  switch (type) {\n  case 'b':\n    return 2;\n  case 'o':\n    return 8;\n  case 'd':\n  case no_type:\n    return 10;\n  case 'x':\n    return 16;\n  default:\n    EMIO_Z_DEV_ASSERT(false);\n    EMIO_Z_INTERNAL_UNREACHABLE;\n  }\n}\n\ninline constexpr result<void> parse_alternate_form(reader& in, int base) noexcept {\n  EMIO_TRYV(in.read_if_match_char('0'));\n\n  if (base == 8) {\n    return success;\n  }\n  EMIO_TRY(const char c, in.read_char());\n\n  if (base == 2) {\n    if (c == 'b' || c == 'B') {\n      return success;\n    }\n    return err::invalid_data;\n  }\n\n  // Base 16.\n  if (c == 'x' || c == 'X') {\n    return success;\n  }\n  return err::invalid_data;\n}\n\ntemplate <typename Arg>\n  requires(std::is_integral_v<Arg> && !std::is_same_v<Arg, bool> && !std::is_same_v<Arg, char>)\nconstexpr result<void> read_arg(reader& original_in, const format_specs& specs, Arg& arg) noexcept {\n  reader in = original_in;\n  if (specs.width != no_width) {\n    if (in.cnt_remaining() < static_cast<size_t>(specs.width)) {\n      return err::eof;\n    }\n    EMIO_TRY(in, in.subreader(0, static_cast<size_t>(specs.width)));\n  }\n\n  EMIO_TRY(const bool is_negative, parse_sign(in));\n\n  int base = 0;\n  if (specs.type == no_type && specs.alternate_form) {\n    EMIO_TRY(base, determine_base(in));\n    if (base != 10) {\n      // Discard alternate form from input.\n      if (base == 8) {\n        in.pop(1);\n      } else {\n        in.pop(2);\n      }\n      EMIO_TRYV(disallow_sign(in));\n    }\n  } else {\n    base = get_base(specs.type);\n    if (specs.alternate_form && base != 10) {\n      EMIO_TRYV(parse_alternate_form(in, base));\n      EMIO_TRYV(disallow_sign(in));\n    }\n  }\n  EMIO_TRY(arg, parse_int<Arg>(in, base, is_negative));\n\n  if (specs.width != no_width) {\n    if (!in.eof()) {\n      return err::invalid_data;\n    }\n    original_in.pop(static_cast<size_t>(specs.width));\n  } else {\n    original_in = in;\n  }\n  return success;\n}\n\ntemplate <typename Arg>\n  requires(std::is_same_v<Arg, char>)\nconstexpr result<void> read_arg(reader& in, const format_specs& /*unused*/, Arg& arg) noexcept {\n  EMIO_TRY(arg, in.read_char());\n  return success;\n}\n\ninline constexpr result<void> read_string_complex(reader& in, const std::string_view format_str,\n                                                  std::string_view& arg) noexcept {\n  // The following algorithm compares the chars of the format string (`format`) against the chars of the input string\n  // (`in`).\n  // The chars are compared one by one (#1).\n  // The `format` contains at least one escape sequence of '{{' or '}}', therefor, at least one char in `format` must be\n  // skipped (#2).\n  // If there is a missmatch, the chars of `format` starts from the beginning but `in` remains unchanged (#3).\n  // The algorithm ends successfully if:\n  // - all chars of `format` are found inside `in` (#4)\n  // - chars in `format` are found inside `in` and the next chars in `format` is another replacement field (#5)\n  // The algorithm terminates without success if all chars of `in` has been compared (#6).\n\n  const char* const format_begin = detail::begin(format_str);\n  const char* format_it = format_begin;\n  const char* const format_end = detail::end(format_str);\n\n  const std::string_view in_remaining = in.view_remaining();\n  const char* const in_begin = detail::begin(in_remaining);\n  const char* in_it = in_begin;\n  const char* const in_end = detail::end(in_remaining);\n\n  size_t matches_cnt = 0;  // Count number matches.\n  while (true) {\n    if (format_it == format_end) {\n      break;  // Complete spec matches input. Succeed. #4\n    }\n    if (in_it == in_end) {\n      return err::invalid_data;  // Reached end of input. Fail. #6\n    }\n\n    // If there is an escape sequence, skip one spec char. #2\n    if (*format_it == '{') {\n      EMIO_Z_DEV_ASSERT((format_it + 1) != format_end);  // Spec is already validated.\n      if (*(format_it + 1) != '{') {                     // New replacement field.\n        break;                                           // Spec matches input. Succeed. #5\n      }\n      format_it += 1;                        // Skip escaped one.\n      EMIO_Z_DEV_ASSERT(*format_it == '{');  // Must be '{'.\n    } else if (*format_it == '}') {\n      EMIO_Z_DEV_ASSERT((format_it + 1) != format_end);  // Spec is already validated.\n      format_it += 1;                                    // Skip escaped one.\n      EMIO_Z_DEV_ASSERT(*format_it == '}');              // Must be '}'.\n    }\n\n    if (*in_it == *format_it) {  // If input and spec match, check next spec char. #1\n      ++format_it;\n      ++matches_cnt;\n    } else {  // Otherwise start from the beginning with the spec. #3\n      format_it = format_begin;\n      matches_cnt = 0;\n    }\n    ++in_it;\n  }\n  // `in` and `format` matches. Capture string.\n  arg = std::string_view{in_begin, in_it - matches_cnt};\n  in.pop(arg.size());\n  return success;\n}\n\ninline constexpr result<void> read_string(reader& in, format_specs& specs, reader& format_rdr,\n                                          std::string_view& arg) noexcept {\n  // There exists 5 cases on how to read a string.\n  // 1) The string spec has specified an exact width.\n  // 2) The remaining string spec is empty, read everything.\n  // 3) The remaining string spec does not contain any possible escape sequence ('{{' or '}}'), read until match.\n  // 4) The remaining string spec does contain a possible escape sequence, but it turns out, it is the replacement\n  //    field.\n  // 5) The remaining string spec does contain at least one escape sequence.\n\n  if (specs.width != no_width) {  // 1)\n    EMIO_TRY(arg, in.read_n_chars(static_cast<size_t>(specs.width)));\n    return success;\n  }\n  const result<std::string_view> until_next_res = format_rdr.read_until_any_of(\"{}\", {.keep_delimiter = true});\n  if (until_next_res == err::eof) {  // Read everything. 2)\n    arg = in.read_remaining();\n    return success;\n  }\n\n  const result<char> next_char_res = format_rdr.read_char();\n  const auto is_replacement_field = [&]() noexcept {  // 4)\n    const char next_char = next_char_res.assume_value();\n    const char over_next_char = format_rdr.read_char().assume_value();  // Spec is validated.\n    return next_char == '{' && over_next_char != '{';\n  };\n\n  if (next_char_res == err::eof /* 3) */ || is_replacement_field()) {\n    EMIO_TRY(arg, in.read_until_str(until_next_res.assume_value(), {.keep_delimiter = true}));\n    return success;\n  }\n  format_rdr.unpop(2);                                               // Undo replacement field check from 4).\n  return read_string_complex(in, format_rdr.view_remaining(), arg);  // 5)\n}\n\n//\n// Checks.\n//\n\n// specs is passed by reference instead as return type to reduce copying of big value (and code bloat)\ninline constexpr result<void> validate_format_specs(reader& format_rdr, format_specs& specs) noexcept {\n  EMIO_TRY(char c, format_rdr.read_char());\n  if (c == '}') {  // Scan end.\n    return success;\n  }\n\n  if (c == '#') {  // Alternate form.\n    specs.alternate_form = true;\n    EMIO_TRY(c, format_rdr.read_char());\n  }\n  if (isdigit(c)) {  // Width.\n    format_rdr.unpop();\n    EMIO_TRY(const uint32_t size, format_rdr.parse_int<uint32_t>());\n    if (size == 0 || size > (static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))) {\n      return err::invalid_format;\n    }\n    specs.width = static_cast<int32_t>(size);\n    EMIO_TRY(c, format_rdr.read_char());\n  }\n  if (detail::isalpha(c)) {\n    specs.type = c;\n    EMIO_TRY(c, format_rdr.read_char());\n  }\n  if (c == '}') {  // Scan end.\n    return success;\n  }\n  return err::invalid_format;\n}\n\ninline constexpr result<void> parse_format_specs(reader& format_rdr, format_specs& specs) noexcept {\n  char c = format_rdr.read_char().assume_value();\n  if (c == '}') {  // Scan end.\n    return success;\n  }\n\n  if (c == '#') {  // Alternate form.\n    specs.alternate_form = true;\n    c = format_rdr.read_char().assume_value();\n  }\n  if (isdigit(c)) {  // Width.\n    format_rdr.unpop();\n    specs.width = static_cast<int32_t>(format_rdr.parse_int<uint32_t>().assume_value());\n    c = format_rdr.read_char().assume_value();\n  }\n  if (detail::isalpha(c)) {\n    specs.type = c;\n    format_rdr.pop();  // format_rdr.read_char() in validate_format_specs;\n  }\n  return success;\n}\n\ninline constexpr result<void> check_char_specs(const format_specs& specs) noexcept {\n  if ((specs.type != no_type && specs.type != 'c') || specs.alternate_form || specs.width > 1) {\n    return err::invalid_format;\n  }\n  return success;\n}\n\ninline constexpr result<void> check_integral_specs(const format_specs& specs) noexcept {\n  switch (specs.type) {\n  case no_type:\n  case 'b':\n  case 'd':\n  case 'o':\n  case 'x':\n    return success;\n  default:\n    return err::invalid_format;\n  }\n}\n\ninline constexpr result<void> check_string_specs(const format_specs& specs) noexcept {\n  if ((specs.type != no_type && specs.type != 's') || specs.alternate_form) {\n    return err::invalid_format;\n  }\n  return success;\n}\n\n//\n// Type traits.\n//\n\n// Specifies if T has an enabled scanner specialization.\ntemplate <typename Arg>\ninline constexpr bool has_scanner_v = std::is_constructible_v<scanner<Arg>>;\n\ntemplate <typename T>\nconcept has_validate_function_v = requires {\n  { scanner<T>::validate(std::declval<reader&>()) } -> std::same_as<result<void>>;\n};\n\ntemplate <typename T>\nconcept has_static_validate_function_v = requires { &scanner<T>::validate; };\n\ntemplate <typename T>\nconcept has_member_validate_function_v = requires { std::declval<scanner<T>>().validate(std::declval<reader&>()); };\n\ntemplate <typename T>\nconcept has_any_validate_function_v =\n    has_static_validate_function_v<T> || std::is_member_function_pointer_v<decltype(&scanner<T>::validate)> ||\n    has_member_validate_function_v<T>;\n\ntemplate <typename T>\ninline constexpr bool is_core_type_v =\n    std::is_same_v<T, char> || std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t> ||\n    std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>;\n\n}  // namespace detail::scan\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/detail/scan/specs.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <cstdint>\n\n#include \"../../reader.hpp\"\n\nnamespace emio::detail::scan {\n\ninline constexpr char no_type = 0;\ninline constexpr int no_width = -1;\n\nstruct format_specs {\n  bool alternate_form{false};\n  char type{no_type};\n  int32_t width{no_width};\n};\n\n}  // namespace emio::detail::scan\n"
  },
  {
    "path": "include/emio/detail/utf.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <span>\n#include <string_view>\n#include <type_traits>\n\n#include \"../buffer.hpp\"\n#include \"conversion.hpp\"\n\nnamespace emio::detail {\n\ninline constexpr bool needs_escape(uint32_t cp) noexcept {\n  return cp < 0x20 || cp >= 0x7f || cp == '\\'' || cp == '\"' || cp == '\\\\';\n}\n\ninline constexpr size_t count_size_when_escaped(std::string_view sv) noexcept {\n  size_t count = 0;\n  for (const char c : sv) {\n    if (!needs_escape(static_cast<uint32_t>(c))) {\n      count += 1;\n    } else if (c == '\\n' || c == '\\r' || c == '\\t' || c == '\\\\' || c == '\\'' || c == '\"') {\n      count += 2;\n    } else {\n      count += 2 + 2 * sizeof(char);  // \\xAB...\n    }\n  }\n  return count;\n}\n\n/*\n * Class which helps to escape a long string in smaller chunks.\n */\nclass write_escaped_helper {\n public:\n  constexpr write_escaped_helper(std::string_view sv) noexcept\n      : src_it_{detail::begin(sv)}, src_end_{detail::end(sv)} {}\n\n  [[nodiscard]] constexpr size_t write_escaped(std::span<char> area) noexcept {\n    char* dst_it = area.data();\n    const char* const dst_end = area.data() + area.size();\n\n    // Write remainder from temporary buffer.\n    const auto write_remainder = [&, this]() noexcept {\n      while (remainder_it_ != remainder_end_ && dst_it != dst_end) {\n        *(dst_it++) = *(remainder_it_++);\n      }\n    };\n    write_remainder();\n\n    while (src_it_ != src_end_) {\n      if (dst_it == dst_end) {\n        return static_cast<size_t>(dst_it - area.data());\n      }\n      const char c = *src_it_++;\n      if (!needs_escape(static_cast<uint32_t>(c))) {\n        *(dst_it++) = c;\n      } else {\n        *(dst_it++) = '\\\\';\n        const auto remaining_space = static_cast<size_t>(dst_end - dst_it);\n        if (remaining_space >= 3) {\n          dst_it = write_escaped(c, dst_it);\n        } else {\n          // Write escaped sequence to remainder.\n          remainder_it_ = detail::begin(remainder_storage_);\n          remainder_end_ = write_escaped(c, remainder_it_);\n          // Write as much as possible into dst.\n          write_remainder();\n        }\n      }\n    }\n    return static_cast<size_t>(dst_it - area.data());\n  }\n\n private:\n  [[nodiscard]] static inline constexpr char* write_escaped(const char c, char* out) noexcept {\n    switch (c) {\n    case '\\n':\n      *(out++) = 'n';\n      return out;\n    case '\\r':\n      *(out++) = 'r';\n      return out;\n    case '\\t':\n      *(out++) = 't';\n      return out;\n    case '\\\\':\n      *(out++) = '\\\\';\n      return out;\n    case '\\'':\n      *(out++) = '\\'';\n      return out;\n    case '\"':\n      *(out++) = '\"';\n      return out;\n    default: {\n      // Escape char zero filled like: \\x05\n      *(out++) = 'x';\n      const auto abs = detail::to_absolute(detail::to_unsigned(c));\n      const size_t number_of_digits = count_digits<16>(abs);\n      // Fill up with zeros.\n      for (size_t i = 0; i < 2 * sizeof(char) - number_of_digits; i++) {\n        *(out++) = '0';\n      }\n      out += to_signed(number_of_digits);\n      write_number(abs, 16, false, out);\n      return out;\n    }\n    }\n  }\n\n  const char* src_it_;  // Input to encode.\n  const char* src_end_;\n  std::array<char, 4> remainder_storage_{};  // Remainder containing data for the next iteration.\n  char* remainder_it_{};\n  char* remainder_end_{};\n};\n\ninline constexpr result<void> write_str_escaped(buffer& buf, std::string_view sv, size_t escaped_size,\n                                                const char quote) {\n  // Perform escaping in multiple chunks, to support buffers with an internal cache.\n  detail::write_escaped_helper helper{sv};\n  EMIO_TRY(auto area, buf.get_write_area_of_max(escaped_size + 2 /*both quotes*/));\n  // Start quote.\n  area[0] = quote;\n  area = area.subspan(1);\n\n  while (true) {\n    const size_t written = helper.write_escaped(area);\n    escaped_size -= written;\n    if (escaped_size == 0) {\n      area = area.subspan(written);\n      break;\n    }\n    EMIO_TRY(area, buf.get_write_area_of_max(escaped_size + 1 /*end quote*/));\n  }\n  if (area.empty()) {\n    EMIO_TRY(area, buf.get_write_area_of_max(1 /*end quote*/));\n  }\n  // End quote.\n  area[0] = quote;\n  return success;\n}\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/detail/validated_string.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <string_view>\n#include <type_traits>\n\n#include \"parser.hpp\"\n#include \"validated_string_storage.hpp\"\n\nnamespace emio {\n\nnamespace detail {\n\n/**\n * This class represents a not yet validated format/scan string, which has to be validated at runtime.\n */\nclass runtime_string {\n public:\n  /**\n   * Constructs an empty runtime format/scan string.\n   */\n  constexpr runtime_string() = default;\n\n  // Don't allow temporary strings or any nullptr.\n  constexpr runtime_string(std::nullptr_t) = delete;\n  constexpr runtime_string(int) = delete;\n#if __STDC_HOSTED__\n  // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved): as intended\n  constexpr runtime_string(std::string&&) = delete;\n#endif\n\n  /**\n   * Constructs the runtime format/scan string from any suitable char sequence.\n   * @param str The char sequence.\n   */\n  template <typename S>\n    requires(std::is_constructible_v<std::string_view, S>)\n  constexpr explicit runtime_string(const S& str) : str_{str} {}\n\n  /**\n   * Obtains a view over the runtime format/scan string.\n   * @return The view.\n   */\n  [[nodiscard]] constexpr std::string_view get() const noexcept {\n    return str_;\n  }\n\n private:\n  std::string_view str_;\n};\n\ntemplate <typename Trait, typename... Args>\nclass valid_string;\n\n/**\n * This class represents a validated format/scan string. The format/scan string is either valid or not.\n * @note The validation happens at object construction.\n * @tparam Args The argument types to format.\n */\ntemplate <typename Trait, typename... Args>\nclass validated_string : public validated_string_storage {\n public:\n  /**\n   * Constructs and validates the format/scan string from any suitable char sequence at compile-time.\n   * @note Terminates compilation if format/scan string is invalid.\n   * @param s The char sequence.\n   */\n  template <typename S>\n    requires(std::is_constructible_v<std::string_view, S>)\n  consteval validated_string(const S& s) noexcept\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay): can construct a std::string_view\n      : validated_string_storage{validated_string_storage::from<Trait, Args...>(s)} {\n    if (get().has_error()) {\n      std::terminate();\n    }\n  }\n\n  /**\n   * Constructs and validates a runtime format/scan string at runtime.\n   * @param s The runtime format/scan string.\n   */\n  constexpr validated_string(const runtime_string& s) noexcept\n      : validated_string_storage{validated_string_storage::from<Trait, Args...>(s.get())} {}\n\n  /**\n   * Returns format/scan string as valid one.\n   * @return The valid format/scan string or invalid_format if the validation failed.\n   */\n  // NOLINTNEXTLINE(modernize-use-nodiscard): result<...> is already declared with nodiscard\n  constexpr result<valid_string<Trait, Args...>> as_valid() const noexcept {\n    if (get().has_value()) {\n      return valid_string<Trait, Args...>{*this};\n    }\n    return err::invalid_format;\n  }\n\n protected:\n  constexpr explicit validated_string(const validated_string_storage& str) noexcept : validated_string_storage{str} {}\n};\n\n/**\n * This class represents a validated format/scan string. The format/scan string can only be valid.\n * @tparam Args The argument types to format.\n */\ntemplate <typename Trait, typename... Args>\nclass valid_string : public validated_string<Trait, Args...> {\n public:\n  /**\n   * Constructs and validates a format/scan string at runtime.\n   * @param s The format/scan string.\n   * @return The valid format/scan string or invalid_format if the validation failed.\n   */\n  template <typename S>\n    requires(std::is_constructible_v<std::string_view, S>)\n  static constexpr result<valid_string<Trait, Args...>> from(const S& s) noexcept {\n    validated_string_storage storage = validated_string_storage::from<Trait, Args...>(s);\n    if (storage.get().has_value()) {\n      return valid_string{storage};\n    }\n    return err::invalid_format;\n  }\n\n  /**\n   * Constructs and validates the format/scan string from any suitable char sequence at compile-time.\n   * @note Terminates compilation if format/scan string is invalid.\n   * @param s The char sequence.\n   */\n  template <typename S>\n    requires(std::is_constructible_v<std::string_view, S>)\n  consteval valid_string(const S& s) noexcept : validated_string<Trait, Args...>{s} {}\n\n private:\n  friend class validated_string<Trait, Args...>;\n\n  constexpr explicit valid_string(const validated_string_storage& str) noexcept\n      : validated_string<Trait, Args...>{str} {}\n};\n\n}  // namespace detail\n\n// Alias template types.\nusing runtime_string = detail::runtime_string;\n\n/**\n * @brief Constructs a runtime string from a given format/scan string.\n * @param s The format/scan string.\n * @return The runtime string.\n */\ninline constexpr runtime_string runtime(const std::string_view& s) noexcept {\n  return runtime_string{s};\n}\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/detail/validated_string_storage.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <cstring>\n#include <string_view>\n\n#include \"../result.hpp\"\n\nnamespace emio::detail {\n\ninline constexpr bool check_if_plain_string(const std::string_view& s) noexcept {\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    return s.find_first_of(\"{}\"sv) == npos;\n  } else {\n    return std::memchr(s.data(), static_cast<int>('{'), s.size()) == nullptr &&\n           std::memchr(s.data(), static_cast<int>('}'), s.size()) == nullptr;\n  }\n}\n\nclass validated_string_storage {\n public:\n  template <typename Trait, typename... Args>\n  static constexpr validated_string_storage from(const std::string_view& s) noexcept {\n    if constexpr (sizeof...(Args) == 0) {\n      if (check_if_plain_string(s)) {\n        return {{true, s}};\n      }\n    }\n    if (Trait::template validate_string<Args...>(s)) {\n      return {{false, s}};\n    }\n    return {{false, err::invalid_format}};\n  }\n\n  constexpr validated_string_storage() noexcept = default;\n\n  /**\n   * Returns if it is just a plain string.\n   * @return True, if the string does not contain any escape sequences or replacement fields, otherwise false.\n   */\n  [[nodiscard]] constexpr bool is_plain_str() const noexcept {\n    return str_.first;\n  }\n\n  /**\n   * Returns if it is an empty string.\n   * @return True, if the string is empty, otherwise false.\n   */\n  [[nodiscard]] constexpr bool empty() const noexcept {\n    return is_plain_str() && str_.second->empty();\n  }\n\n  /**\n   * Returns the validated format/scan string.\n   * @return The view or invalid_format if the validation failed.\n   */\n  constexpr result<std::string_view> get() const noexcept {\n    return str_.second;\n  }\n\n private:\n  // NOLINTNEXTLINE(modernize-pass-by-value): false-positive since no dynamic allocation takes place\n  constexpr validated_string_storage(const std::pair<bool, result<std::string_view>>& str) noexcept : str_{str} {}\n\n  // Wonder why pair and not two variables? Look at this bug report: https://github.com/llvm/llvm-project/issues/67731\n  std::pair<bool, result<std::string_view>> str_{true, \"\"};\n};\n\n}  // namespace emio::detail\n"
  },
  {
    "path": "include/emio/emio.hpp",
    "content": "/*\n  em{io} is a safe and fast high-level and low-level character input/output\n  library for bare-metal and RTOS based embedded systems with a very small\n  binary footprint.\n\n  Copyright (c) 2021 - present, Toni Neubert (viatorus/emio)\n  Copyright (c) 2012 - present, Victor Zverovich (fmtlib/fmt)\n\n  Permission is hereby granted, free of charge, to any person obtaining\n  a copy of this software and associated documentation files (the\n  \"Software\"), to deal in the Software without restriction, including\n  without limitation the rights to use, copy, modify, merge, publish,\n  distribute, sublicense, and/or sell copies of the Software, and to\n  permit persons to whom the Software is furnished to do so, subject to\n  the following conditions:\n\n  The above copyright notice and this permission notice shall be\n  included in all copies or substantial portions of the Software.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n  --- Optional exception to the license ---\n\n  As an exception, if, as a result of your compiling your source code, portions\n  of this Software are embedded into a machine-executable object form of such\n  source code, you may redistribute such embedded portions in such object form\n  without including the above copyright and permission notices.\n */\n\n#ifndef EMIO_Z_MAIN_H\n#define EMIO_Z_MAIN_H\n\n#include \"format.hpp\"\n#include \"ranges.hpp\"\n#include \"scan.hpp\"\n#include \"std.hpp\"\n\n#endif  // EMIO_Z_MAIN_H\n"
  },
  {
    "path": "include/emio/format.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <cstdio>\n\n#include \"detail/format/format_to.hpp\"\n#include \"iterator.hpp\"\n\nnamespace emio {\n\n/**\n * Provides access to the format string and the arguments to format.\n * @note This type should only be \"constructed\" via make_format_args(format_str, args...) and passed directly to a\n * formatting function.\n */\nusing format_args = detail::format::format_args;\n\n// Alias template types.\ntemplate <typename... Args>\nusing format_string = detail::format::format_string<Args...>;\n\ntemplate <typename... Args>\nusing valid_format_string = detail::format::valid_format_string<Args...>;\n\n/**\n * Returns an object that stores a format string with an array of all arguments to format.\n *\n * @note The storage uses reference semantics and does not extend the lifetime of args. It is the programmer's\n * responsibility to ensure that args outlive the return value. Usually, the result is only used as argument to a\n * formatting function taking format_args by reference.\n *\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return Internal type. Implicit convertible to format_args.\n */\ntemplate <typename... Args>\n[[nodiscard]] detail::args_storage<detail::format::format_arg, sizeof...(Args)> make_format_args(\n    const emio::format_string<Args...>& format_str, const Args&... args) noexcept {\n  return {format_str, args...};\n}\n\n/**\n * Determines the total number of characters in the formatted string by formatting args according to the format string.\n * @param args The format args with the format string.\n * @return The total number of characters in the formatted string or invalid_format if the format string validation\n * failed.\n */\ninline result<size_t> vformatted_size(const format_args& args) noexcept {\n  detail::counting_buffer buf{};\n  EMIO_TRYV(detail::format::vformat_to(buf, args));\n  return buf.count();\n}\n\n/**\n * Determines the total number of characters in the formatted string by formatting args according to the format string.\n * @note This is an optimized version where the formatting can never fail.\n * @param format_str The format string\n * @param args The arguments to be formatted.\n * @return The total number of characters in the formatted string.\n */\ntemplate <typename... Args>\n  requires(!format_can_fail_v<Args> && ...)\n[[nodiscard]] constexpr size_t formatted_size(const valid_format_string<Args...>& format_str,\n                                              const Args&... args) noexcept(detail::exceptions_disabled) {\n  detail::counting_buffer buf{};\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    detail::format::format_to(buf, format_str, args...).value();\n  } else {\n    detail::format::vformat_to(buf, emio::make_format_args(format_str, args...)).value();\n  }\n  return buf.count();\n}\n\n/**\n * Determines the total number of characters in the formatted string by formatting args according to the format string.\n * @param format_str The format string\n * @param args The arguments to be formatted.\n * @return The total number of characters in the formatted string.\n */\ntemplate <typename... Args>\n  requires(sizeof...(Args) > 0 && (format_can_fail_v<Args> && ...))\n[[nodiscard]] constexpr result<size_t> formatted_size(const valid_format_string<Args...>& format_str,\n                                                      const Args&... args) noexcept(detail::exceptions_disabled) {\n  detail::counting_buffer buf{};\n  emio::format_string<Args...> str{format_str};\n  EMIO_TRYV(detail::format::format_to(buf, str, args...));\n  return buf.count();\n}\n\n/**\n * Determines the total number of characters in the formatted string by formatting args according to the format string.\n * @param format_str The format string\n * @param args The arguments to be formatted.\n * @return The total number of characters in the formatted string on success or invalid_format if the format string\n * validation failed.\n */\ntemplate <typename T, typename... Args>\n  requires(std::is_same_v<T, runtime_string> || std::is_same_v<T, emio::format_string<Args...>>)\nconstexpr result<size_t> formatted_size(const T& format_str, const Args&... args) noexcept {\n  detail::counting_buffer buf{};\n  emio::format_string<Args...> str{format_str};\n  EMIO_TRYV(detail::format::format_to(buf, str, args...));\n  return buf.count();\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the output buffer.\n * @param buf The output buffer.\n * @param args The format args with the format string.\n * @return Success or EOF if the buffer is to small or invalid_format if the format string validation failed.\n */\ninline result<void> vformat_to(buffer& buf, const format_args& args) noexcept {\n  EMIO_TRYV(detail::format::vformat_to(buf, args));\n  return success;\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the writer's buffer.\n * @param out The output writer.\n * @param args The format args with the format string.\n * @return Success or EOF if the buffer is to small or invalid_format if the format string validation failed.\n */\ninline result<void> vformat_to(writer& out, const format_args& args) noexcept {\n  EMIO_TRYV(detail::format::vformat_to(out.get_buffer(), args));\n  return success;\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the output iterator.\n * @param out The output iterator.\n * @param args The format args with the format string.\n * @return The iterator past the end of the output range on success or EOF if the buffer is to small or invalid_format\n * if the format string validation failed.\n */\ntemplate <typename OutputIt, typename... Args>\n  requires(std::output_iterator<OutputIt, char>)\nconstexpr result<OutputIt> vformat_to(OutputIt out, const format_args& args) noexcept {\n  iterator_buffer buf{out};\n  EMIO_TRYV(detail::format::vformat_to(buf, args));\n  return buf.out();\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the output buffer.\n * @param buf The output buffer.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return Success or EOF if the buffer is to small or invalid_format if the format string validation failed.\n */\ntemplate <typename... Args>\nconstexpr result<void> format_to(buffer& buf, const emio::format_string<Args...>& format_str,\n                                 const Args&... args) noexcept {\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    EMIO_TRYV(detail::format::format_to(buf, format_str, args...));\n  } else {\n    EMIO_TRYV(detail::format::vformat_to(buf, emio::make_format_args(format_str, args...)));\n  }\n  return success;\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the writer's buffer.\n * @param out The writer.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return Success or EOF if the buffer is to small or invalid_format if the format string validation failed.\n */\ntemplate <typename... Args>\nconstexpr result<void> format_to(writer& out, const emio::format_string<Args...>& format_str,\n                                 const Args&... args) noexcept {\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    EMIO_TRYV(detail::format::format_to(out.get_buffer(), format_str, args...));\n  } else {\n    EMIO_TRYV(detail::format::vformat_to(out.get_buffer(), emio::make_format_args(format_str, args...)));\n  }\n  return success;\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the output iterator.\n * @param out The output iterator.\n * @param args The format args with the format string.\n * @return The iterator past the end of the output range on success or EOF if the buffer is to small or invalid_format\n * if the format string validation failed.\n */\ntemplate <typename OutputIt, typename... Args>\n  requires(std::output_iterator<OutputIt, char>)\nconstexpr result<OutputIt> format_to(OutputIt out, const emio::format_string<Args...>& format_str,\n                                     const Args&... args) noexcept {\n  iterator_buffer buf{out};\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    EMIO_TRYV(detail::format::format_to(buf, format_str, args...));\n  } else {\n    EMIO_TRYV(detail::format::vformat_to(buf, emio::make_format_args(format_str, args...)));\n  }\n  return buf.out();\n}\n\n#if __STDC_HOSTED__\n/**\n * Formats arguments according to the format string, and returns the result as string.\n * @param args The format args with the format string.\n * @return The string on success or invalid_format if the format string validation\n * failed.\n */\ninline result<std::string> vformat(const format_args& args) noexcept {\n  memory_buffer buf;\n  if (auto res = detail::format::vformat_to(buf, args); !res) {\n    return res.assume_error();\n  }\n  return buf.str();\n}\n\n/**\n * Formats arguments according to the format string, and returns the result as string.\n * @note This is an optimized version where the formatting can never fail.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return The string.\n */\ntemplate <typename... Args>\n  requires(!format_can_fail_v<Args> && ...)\n[[nodiscard]] std::string format(const emio::valid_format_string<Args...>& format_str,\n                                 const Args&... args) noexcept(detail::exceptions_disabled) {\n  return emio::vformat(emio::make_format_args(format_str, args...)).value();  // Should never fail.\n}\n\n/**\n * Formats arguments according to the format string, and returns the result as string.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return The string on success or invalid_format if the format string validation failed.\n */\ntemplate <typename... Args>\n  requires(sizeof...(Args) > 0 && (format_can_fail_v<Args> && ...))\n[[nodiscard]] result<std::string> format(const emio::valid_format_string<Args...>& format_str,\n                                         const Args&... args) noexcept {\n  return emio::vformat(emio::make_format_args(format_str, args...));\n}\n\n/**\n * Formats arguments according to the format string, and returns the result as string.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return The string on success or invalid_format if the format string validation failed.\n */\ntemplate <typename T, typename... Args>\n  requires(std::is_same_v<T, runtime_string> || std::is_same_v<T, emio::format_string<Args...>>)\nresult<std::string> format(T format_str, const Args&... args) noexcept {\n  return emio::vformat(emio::make_format_args(format_str, args...));\n}\n#endif\n\n/**\n * Return type of (v)format_to_n functions.\n * @tparam OutputIt The output iterator type.\n */\ntemplate <typename OutputIt>\nstruct format_to_n_result {\n  OutputIt out;                           ///< The iterator past the end.\n  std::iter_difference_t<OutputIt> size;  ///< The total number (not truncated) output size.\n};\n\n/**\n * Formats arguments according to the format string, and writes the result to the output iterator. At most n characters\n * are written.\n * @param out The output iterator.\n * @param n The maximum number of characters to be written to the output iterator.\n * @param args The format args with the format string.\n * @return The format_to_n_result on success or invalid_format if the format string validation failed.\n */\ntemplate <typename OutputIt>\n  requires(std::output_iterator<OutputIt, char>)\nresult<format_to_n_result<OutputIt>> vformat_to_n(OutputIt out, std::iter_difference_t<OutputIt> n,\n                                                  const format_args& args) noexcept {\n  truncating_iterator tout{out, static_cast<size_t>(n)};\n  iterator_buffer buf{tout};\n  EMIO_TRYV(detail::format::vformat_to(buf, args));\n  EMIO_TRYV(buf.flush());\n  tout = buf.out();\n  return format_to_n_result<OutputIt>{tout.out(), tout.count()};\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the buffer. At most n characters are\n * written.\n * @param buf The buffer.\n * @param n The maximum number of characters to be written to the buffer.\n * @param args The format args with the format string.\n * @return The total number (not truncated) output size on success or EOF if the buffer is to small or invalid_format if\n * the format string validation failed.\n */\ntemplate <typename... Args>\nresult<size_t> vformat_to_n(buffer& buf, size_t n, const format_args& args) noexcept {\n  truncating_buffer trunc_buf{buf, n};\n  EMIO_TRYV(detail::format::vformat_to(trunc_buf, args));\n  EMIO_TRYV(trunc_buf.flush());\n  return trunc_buf.count();\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the output iterator. At most n characters\n * are written.\n * @param out The output iterator.\n * @param n The maximum number of characters to be written to the output iterator.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return The format_to_n_result on success or invalid_format if the format string validation failed.\n */\ntemplate <typename OutputIt, typename... Args>\n  requires(std::output_iterator<OutputIt, char>)\nconstexpr result<format_to_n_result<OutputIt>> format_to_n(OutputIt out, std::iter_difference_t<OutputIt> n,\n                                                           const emio::format_string<Args...>& format_str,\n                                                           const Args&... args) noexcept {\n  truncating_iterator tout{out, static_cast<size_t>(n)};\n  iterator_buffer buf{tout};\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    EMIO_TRYV(detail::format::format_to(buf, format_str, args...));\n  } else {\n    EMIO_TRYV(detail::format::vformat_to(buf, emio::make_format_args(format_str, args...)));\n  }\n  EMIO_TRYV(buf.flush());\n  tout = buf.out();\n  return format_to_n_result<OutputIt>{tout.out(), tout.count()};\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the buffer. At most n characters are\n * written.\n * @param buf The buffer.\n * @param n The maximum number of characters to be written to the buffer.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return The total number (not truncated) output size on success or EOF if the buffer is to small or invalid_format if\n * the format string validation failed.\n */\ntemplate <typename... Args>\nconstexpr result<size_t> format_to_n(buffer& buf, size_t n, const emio::format_string<Args...>& format_str,\n                                     const Args&... args) noexcept {\n  truncating_buffer trunc_buf{buf, n};\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    EMIO_TRYV(detail::format::format_to(trunc_buf, format_str, args...));\n  } else {\n    EMIO_TRYV(detail::format::vformat_to(trunc_buf, emio::make_format_args(format_str, args...)));\n  }\n  EMIO_TRYV(trunc_buf.flush());\n  return trunc_buf.count();\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to a file stream.\n * @param file The file stream.\n * @param args The format args with the format string.\n * @return Success or EOF if the file stream is not writable or invalid_format if the format string validation failed.\n */\ninline result<void> vprint(std::FILE* file, const format_args& args) noexcept {\n  if (file == nullptr) {\n    return err::invalid_data;\n  }\n\n  file_buffer buf{file};\n  EMIO_TRYV(detail::format::vformat_to(buf, args));\n  return buf.flush();\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the standard output stream.\n * @note This is an optimized version where the formatting can never fail.\n * @param format_str The format string.\n * @param args The format args with the format string.\n */\ntemplate <typename... Args>\n  requires(!format_can_fail_v<Args> && ...)\nvoid print(const emio::valid_format_string<Args...>& format_str,\n           const Args&... args) noexcept(detail::exceptions_disabled) {\n  vprint(stdout, emio::make_format_args(format_str, args...)).value();  // Should never fail.\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the standard output stream.\n * @param format_str The format string.\n * @param args The format args with the format string.\n * @return Success or EOF if the file stream is not writable or invalid_format if the format string validation failed.\n */\ntemplate <typename... Args>\n  requires(sizeof...(Args) > 0 && (format_can_fail_v<Args> && ...))\nresult<void> print(const emio::valid_format_string<Args...>& format_str, const Args&... args) noexcept {\n  return vprint(stdout, emio::make_format_args(format_str, args...));\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the standard output stream.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return Success or EOF if the file stream is not writable or invalid_format if the format string validation failed.\n */\ntemplate <typename T, typename... Args>\n  requires(std::is_same_v<T, runtime_string> || std::is_same_v<T, emio::format_string<Args...>>)\nresult<void> print(const T& format_str, const Args&... args) noexcept {\n  return vprint(stdout, emio::make_format_args(format_str, args...));\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to a file stream.\n * @param file The file stream.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return Success or EOF if the file stream is not writable or invalid_format if the format string validation failed.\n */\ntemplate <typename... Args>\nresult<void> print(std::FILE* file, const emio::format_string<Args...>& format_str, const Args&... args) noexcept {\n  return vprint(file, emio::make_format_args(format_str, args...));\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to a file stream with a new line at the\n * end.\n * @param file The file stream.\n * @param args The format args with the format string.\n * @return Success or EOF if the file stream is not writable or invalid_format if the format string validation failed.\n */\ninline result<void> vprintln(std::FILE* file, const format_args& args) noexcept {\n  if (file == nullptr) {\n    return err::invalid_data;\n  }\n\n  file_buffer buf{file};\n  EMIO_TRYV(detail::format::vformat_to(buf, args));\n  EMIO_TRY(auto area, buf.get_write_area_of(1));\n  area[0] = '\\n';\n  return buf.flush();\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the standard output stream with a new line\n * at the end.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n */\ntemplate <typename... Args>\n  requires(!format_can_fail_v<Args> && ...)\nvoid println(const emio::valid_format_string<Args...>& format_str,\n             const Args&... args) noexcept(detail::exceptions_disabled) {\n  vprintln(stdout, emio::make_format_args(format_str, args...)).value();  // Should never fail.\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the standard output stream with a new line\n * at the end.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return Success or EOF if the file stream is not writable or invalid_format if the format string validation failed.\n */\ntemplate <typename... Args>\n  requires(sizeof...(Args) > 0 && (format_can_fail_v<Args> && ...))\nresult<void> println(const emio::valid_format_string<Args...>& format_str, const Args&... args) noexcept {\n  return vprintln(stdout, emio::make_format_args(format_str, args...));\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to the standard output stream with a new line\n * at the end.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return Success or EOF if the file stream is not writable or invalid_format if the format string validation failed.\n */\ntemplate <typename T, typename... Args>\n  requires(std::is_same_v<T, runtime_string> || std::is_same_v<T, emio::format_string<Args...>>)\nresult<void> println(const T& format_str, const Args&... args) noexcept {\n  return vprintln(stdout, emio::make_format_args(format_str, args...));\n}\n\n/**\n * Formats arguments according to the format string, and writes the result to a file stream with a new line at the end.\n * @param file The file stream.\n * @param format_str The format string.\n * @param args The arguments to be formatted.\n * @return Success or EOF if the file stream is not writable or invalid_format if the format string validation failed.\n */\ntemplate <typename... Args>\nresult<void> println(std::FILE* file, const emio::format_string<Args...>& format_str, const Args&... args) noexcept {\n  return vprintln(file, emio::make_format_args(format_str, args...));\n}\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/formatter.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <memory>\n\n#include \"detail/format/formatter.hpp\"\n\nnamespace emio {\n\n/**\n * Checks if a type is formattable.\n * @tparam T The type to check.\n */\ntemplate <typename T>\ninline constexpr bool is_formattable_v = detail::format::has_formatter_v<std::remove_cvref_t<T>>;\n\n/**\n * Checks if the format function of a formatter of a type could fail, regardless of the provided buffer size.\n * @tparam T The type to check.\n */\ntemplate <typename T>\ninline constexpr bool format_can_fail_v = detail::format::format_can_fail_v<std::remove_cvref_t<T>>;\n\n/**\n * Class template that defines formatting rules for a given type.\n * @note This class definition is just a mock-up. See other template specialization for a concrete formatting.\n * @tparam T The type to format.\n */\ntemplate <typename T>\nclass formatter {\n public:\n  // Not constructable because this is just a minimal example how to write a custom formatter.\n  formatter() = delete;\n\n  /**\n   * Optional static function to validate the format string syntax for this type.\n   * @note If not present, the parse function is invoked for validation.\n   * @param format_rdr The reader over the format string.\n   * @return Success if the format string is valid.\n   */\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  /**\n   * Function to parse the format specs for this type.\n   * @param format_rdr The reader over the format string.\n   * @return Success if the format string is valid and could be parsed.\n   */\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  /**\n   * Function to format the object of this type according to the parsed format specs.\n   * @param out The output writer.\n   * @param arg The argument to format.\n   * @return Success if the formatting could be done.\n   */\n  constexpr result<void> format(writer& out, const T& arg) const noexcept {\n    return out.write_int(sizeof(arg));\n  }\n\n  /**\n   * Optional variable to indicate that the formatting of T could fail (e.g. not all states of T can be formatted)\n   * regardless of the provided buffer size.\n   * If this variable is set to true, some optimized API calls won't be available for this type.\n   * If this variable is not defined, it is assumed that the formatting can never fail, which is true in almost all\n   * cases.\n   */\n  static constexpr bool format_can_fail = false;\n};\n\n/**\n * Formatter for most common unambiguity types.\n * This includes:\n * - boolean\n * - char\n * - string_view\n * - void* / nullptr\n * - integral, floating-point types\n * @tparam T The type.\n */\ntemplate <typename T>\n  requires(detail::format::is_core_type_v<T>)\nclass formatter<T> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    detail::format::format_specs specs{};\n    EMIO_TRYV(validate_format_specs(format_rdr, specs));\n    if constexpr (std::is_same_v<T, bool>) {\n      EMIO_TRYV(check_bool_specs(specs));\n    } else if constexpr (std::is_same_v<T, char>) {\n      EMIO_TRYV(check_char_specs(specs));\n    } else if constexpr (detail::format::is_void_pointer_v<T> || std::is_null_pointer_v<T>) {\n      EMIO_TRYV(check_pointer_specs(specs));\n    } else if constexpr (std::is_integral_v<T>) {\n      EMIO_TRYV(check_integral_specs(specs));\n      if constexpr (std::is_unsigned_v<T>) {\n        EMIO_TRYV(check_unsigned_specs(specs));\n      }\n    } else if constexpr (std::is_floating_point_v<T>) {\n      EMIO_TRYV(check_floating_point_specs(specs));\n    } else if constexpr (std::is_constructible_v<std::string_view, T>) {\n      EMIO_TRYV(check_string_specs(specs));\n    } else {\n      static_assert(detail::always_false_v<T>, \"Unknown core type!\");\n    }\n    return success;\n  }\n\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return detail::format::parse_format_specs(format_rdr, specs_);\n  }\n\n  constexpr result<void> format(writer& out, const T& arg) const noexcept {\n    auto specs = specs_;  // Copy spec because format could be called multiple times (e.g. ranges).\n    return write_arg(out, specs, arg);\n  }\n\n  /**\n   * Enables or disables the debug output format.\n   * @note Used e.g. from range formatter.\n   * @param enabled Flag to enable or disable the debug output.\n   */\n  constexpr void set_debug_format(bool enabled) noexcept\n    requires(std::is_same_v<T, char> || std::is_same_v<T, std::string_view>)\n  {\n    if (enabled) {\n      specs_.type = '?';\n    } else {\n      specs_.type = detail::format::no_type;\n    }\n  }\n\n  /**\n   * Sets the width.\n   * @note Used e.g. from dynamic spec formatter.\n   * @param width The width.\n   */\n  constexpr void set_width(int32_t width) noexcept {\n    specs_.width = std::max<int32_t>(0, width);\n  }\n\n  /**\n   * Sets the precision.\n   * @note Used e.g. from dynamic spec formatter.\n   * @param precision The precision.\n   */\n  constexpr void set_precision(int32_t precision) noexcept {\n    specs_.precision = std::max<int32_t>(0, precision);\n  }\n\n private:\n  detail::format::format_specs specs_{};\n};\n\n/**\n * Formatter for any type which could be represented as a core type. E.g. string -> string_view.\n * @tparam T The unscoped enum type.\n */\ntemplate <typename T>\n  requires(!detail::format::is_core_type_v<T> && detail::format::is_core_type_v<detail::format::unified_type_t<T>>)\nclass formatter<T> : public formatter<detail::format::unified_type_t<T>> {};\n\n/**\n * Formatter for unscoped enum types to there underlying type.\n * @tparam T The unscoped enum type.\n */\ntemplate <typename T>\n  requires(std::is_enum_v<T> && std::is_convertible_v<T, std::underlying_type_t<T>>)\nclass formatter<T> : public formatter<std::underlying_type_t<T>> {\n public:\n  constexpr result<void> format(writer& out, const T& arg) const noexcept {\n    return formatter<std::underlying_type_t<T>>::format(out, static_cast<std::underlying_type_t<T>>(arg));\n  }\n};\n\n/**\n * Formatter for std::byte.\n */\ntemplate <>\nclass formatter<std::byte> : public formatter<uint8_t> {\n public:\n  constexpr result<void> format(writer& out, const std::byte& arg) const noexcept {\n    return formatter<uint8_t>::format(out, static_cast<uint8_t>(arg));\n  }\n};\n\n/**\n * Formatter for types which can formatted with a format_as function when using ADL.\n * @tparam T The type.\n */\ntemplate <typename T>\n  requires(detail::format::has_format_as<T>)\nclass formatter<T> : public formatter<detail::format::format_as_return_t<T>> {\n public:\n  constexpr result<void> format(writer& out, const T& arg) const noexcept {\n    return formatter<detail::format::format_as_return_t<T>>::format(out, format_as(arg));\n  }\n};\n\nnamespace detail {\n\ntemplate <typename T>\nstruct format_spec_with_value;\n\n}\n\n/**\n * Struct to dynamically specify width and precision.\n */\nstruct format_spec {\n  /// Constant which indicates that the spec should not overwrite the parsed spec from the format string.\n  static constexpr int32_t not_defined = -std::numeric_limits<int32_t>::max();\n\n  /// The width.\n  int32_t width{not_defined};\n  /// The precision.\n  int32_t precision{not_defined};\n\n  /**\n   * Returns an object that holds the value and the dynamic specification as reference.\n   *\n   * @note The object uses reference semantics and does not extend the lifetime of the held objects. It is the\n   * programmer's responsibility to ensure that value outlive the return value. Usually, the result is only used as\n   * argument to a formatting function.\n   *\n   * @param value The value to format.\n   * @return Internal type.\n   */\n  template <typename T>\n  [[nodiscard]] constexpr detail::format_spec_with_value<T> with(const T& value) const noexcept;\n};\n\nnamespace detail {\n\n/**\n * Struct holding the format spec and value.\n */\ntemplate <typename T>\nstruct format_spec_with_value {\n  const format_spec& spec;\n  const T& value;\n};\n\n}  // namespace detail\n\ntemplate <typename T>\n[[nodiscard]] constexpr detail::format_spec_with_value<T> format_spec::with(const T& value) const noexcept {\n  return {*this, value};\n}\n\n/**\n * Formatter for types whose format specification is dynamically defined.\n * @tparam T The underlying type.\n */\ntemplate <typename T>\nclass formatter<detail::format_spec_with_value<T>> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return formatter<T>::validate(format_rdr);\n  }\n\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return underlying_.parse(format_rdr);\n  }\n\n  constexpr result<void> format(writer& out, const detail::format_spec_with_value<T>& arg) noexcept {\n    overwrite_spec(arg.spec);\n    return underlying_.format(out, arg.value);\n  }\n\n private:\n  template <typename F>\n    requires requires(F x) {\n      x.set_width(1);\n      x.set_precision(1);\n    }\n  static constexpr F& get_core_formatter(F& formatter) noexcept {\n    return formatter;\n  }\n\n  template <typename F>\n    requires requires(F x) { x.underlying(); }\n  static constexpr auto& get_core_formatter(F& formatter) noexcept {\n    return get_core_formatter(formatter.underlying());\n  }\n\n  constexpr void overwrite_spec(const format_spec& spec) noexcept {\n    // Overwrite the spec of the core formatter if they are dynamically defined.\n    auto& f = get_core_formatter(underlying_);\n    if (spec.width != format_spec::not_defined) {\n      f.set_width(spec.width);\n    }\n    if (spec.precision != format_spec::not_defined) {\n      f.set_precision(spec.precision);\n    }\n  }\n\n  formatter<T> underlying_{};\n};\n\n/**\n * Converts a value of a pointer-like type to const void * for pointer formatting.\n * @param p The value of the pointer.\n * @return The const void* version of the pointer.\n */\ntemplate <typename T>\n  requires(std::is_pointer_v<T>)\nconstexpr auto ptr(T p) noexcept {\n  if constexpr (std::is_volatile_v<std::remove_pointer_t<T>>) {\n    return static_cast<const volatile void*>(p);\n  } else {\n    return static_cast<const void*>(p);\n  }\n}\n\ntemplate <typename T, typename Deleter>\nconstexpr const void* ptr(const std::unique_ptr<T, Deleter>& p) {\n  return p.get();\n}\n\n#if __STDC_HOSTED__\ntemplate <typename T>\nconst void* ptr(const std::shared_ptr<T>& p) {\n  return p.get();\n}\n#endif\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/iterator.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <iterator>\n\nnamespace emio {\n\n/**\n * This class provides the member types for an output iterator.\n * @tparam OutputIt The iterator type to wrap.\n */\ntemplate <typename OutputIt>\nclass truncating_iterator_base {\n public:\n  using iterator_category = std::output_iterator_tag;\n  using value_type = typename std::iterator_traits<OutputIt>::value_type;\n  using difference_type = std::ptrdiff_t;\n  using pointer = void;\n  using reference = void;\n\n  /**\n   * Default constructs a truncating iterator.\n   */\n  constexpr truncating_iterator_base() = default;\n\n  /**\n   * Constructs a truncating iterator with an output iterator to wrap and a given output limit.\n   * @param out The output iterator.\n   * @param limit The output limit.\n   */\n  constexpr truncating_iterator_base(OutputIt out, size_t limit)\n      : out_{out}, limit_{static_cast<std::iter_difference_t<OutputIt>>(limit)} {}\n\n  /**\n   * Returns the actual output iterator.\n   * @return The output iterator.\n   */\n  constexpr OutputIt out() const {\n    return out_;\n  }\n\n  /**\n   * Returns the count of the total (not truncated) outputted elements.\n   * @return The count.\n   */\n  [[nodiscard]] constexpr std::iter_difference_t<OutputIt> count() const noexcept {\n    return count_;\n  }\n\n protected:\n  OutputIt out_{};\n  std::iter_difference_t<OutputIt> limit_{};\n  std::iter_difference_t<OutputIt> count_{};\n};\n\n/**\n * The truncating iterator is an output iterator which limits the amount of elements passed to an wrapped output\n * iterator.\n * @tparam OutputIt The iterator type to wrap.\n */\ntemplate <typename OutputIt>\nclass truncating_iterator;\n\n// CTAD guide.\ntemplate <typename OutputIt>\ntruncating_iterator(OutputIt&&, size_t) -> truncating_iterator<std::decay_t<OutputIt>>;\n\n/**\n * Template specification for a pure output-only iterator (e.g. the back_insert_iterator).\n * @tparam OutputIt The iterator type to wrap.\n */\ntemplate <typename OutputIt>\n  requires(std::is_void_v<typename std::iterator_traits<OutputIt>::value_type>)\nclass truncating_iterator<OutputIt> : public truncating_iterator_base<OutputIt> {\n public:\n  using truncating_iterator_base<OutputIt>::truncating_iterator_base;\n\n  template <typename T>\n  constexpr truncating_iterator& operator=(T val) {\n    if (this->count_ < this->limit_) {\n      *this->out_++ = val;\n    }\n    this->count_ += 1;\n    return *this;\n  }\n\n  constexpr truncating_iterator& operator++() {\n    return *this;\n  }\n  constexpr truncating_iterator& operator++(int) {\n    return *this;\n  }\n  constexpr truncating_iterator& operator*() {\n    return *this;\n  }\n};\n\n/**\n * Template specification for any other output iterator.\n * @tparam OutputIt The iterator type to wrap.\n */\ntemplate <typename OutputIt>\n  requires(!std::is_void_v<typename std::iterator_traits<OutputIt>::value_type>)\nclass truncating_iterator<OutputIt> : public truncating_iterator_base<OutputIt> {\n public:\n  using value_type = typename truncating_iterator_base<OutputIt>::value_type;\n\n  using truncating_iterator_base<OutputIt>::truncating_iterator_base;\n\n  constexpr truncating_iterator& operator++() {\n    if (this->count_ < this->limit_) {\n      ++this->out_;\n    }\n    this->count_ += 1;\n    return *this;\n  }\n\n  constexpr truncating_iterator operator++(int) {\n    auto it = *this;\n    ++*this;\n    return it;\n  }\n\n  constexpr value_type& operator*() const {\n    if (this->count_ < this->limit_) {\n      return *this->out_;\n    }\n    return black_hole_;\n  }\n\n private:\n  mutable value_type black_hole_{};\n};\n\nnamespace detail {\n\ntemplate <typename Iterator>\nstruct get_value_type;\n\ntemplate <typename Iterator>\nstruct get_value_type<truncating_iterator<Iterator>> : get_value_type<Iterator> {};\n\n}  // namespace detail\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/ranges.hpp",
    "content": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n//\n// Additional licence for this file:\n// Copyright (c) 2018 - present, Remotion (Igor Schulz)\n\n#pragma once\n\n#include \"detail/format/ranges.hpp\"\n\nnamespace emio {\n\n/**\n * Formatter for ranges.\n * @tparam T The type.\n */\ntemplate <typename T>\n  requires(detail::format::is_valid_range<T> && !detail::format::is_contiguous_but_not_span<T>)\nclass formatter<T> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    EMIO_TRY(char c, format_rdr.read_char());\n    if (c == 'n') {\n      EMIO_TRY(c, format_rdr.read_char());\n    }\n    if (c == '}') {\n      return success;\n    }\n    if (c == ':') {\n      return formatter<detail::format::element_type_t<T>>::validate(format_rdr);\n    } else {\n      return err::invalid_format;\n    }\n  }\n\n  constexpr formatter() noexcept\n    requires(!detail::format::is_map<T> && !detail::format::is_set<T>)\n      : specs_{detail::sv(\"[\"), detail::sv(\"]\"), detail::sv(\", \")} {}\n\n  constexpr formatter() noexcept\n    requires(detail::format::is_map<T>)\n      : specs_{detail::sv(\"{\"), detail::sv(\"}\"), detail::sv(\", \")} {\n    underlying_.set_brackets({}, {});\n    underlying_.set_separator(detail::sv(\": \"));\n  }\n\n  constexpr formatter() noexcept\n    requires(detail::format::is_set<T> && !detail::format::is_map<T>)\n      : specs_{detail::sv(\"{\"), detail::sv(\"}\"), detail::sv(\", \")} {}\n\n  constexpr void set_separator(std::string_view separator) noexcept {\n    specs_.separator = separator;\n  }\n\n  constexpr void set_brackets(std::string_view opening_bracket, std::string_view closing_bracket) noexcept {\n    specs_.opening_bracket = opening_bracket;\n    specs_.closing_bracket = closing_bracket;\n  }\n\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    char c = format_rdr.peek().assume_value();\n    if (c == 'n') {\n      set_brackets({}, {});\n      format_rdr.pop();  // n\n      c = format_rdr.peek().assume_value();\n    }\n    if (c == '}') {\n      detail::format::maybe_set_debug_format(underlying_, true);\n    } else {\n      format_rdr.pop();  // :\n    }\n    return underlying_.parse(format_rdr);\n  }\n\n  constexpr result<void> format(writer& out, const T& arg) const noexcept {\n    EMIO_TRYV(out.write_str(specs_.opening_bracket));\n\n    using std::begin;\n    using std::end;\n    auto first = begin(arg);\n    const auto last = end(arg);\n    for (auto it = first; it != last; ++it) {\n      if (it != first) {\n        EMIO_TRYV(out.write_str(specs_.separator));\n      }\n      EMIO_TRYV(underlying_.format(out, *it));\n    }\n    EMIO_TRYV(out.write_str(specs_.closing_bracket));\n    return success;\n  }\n\n  constexpr auto& underlying() noexcept {\n    return underlying_;\n  }\n\n private:\n  formatter<detail::format::element_type_t<T>> underlying_{};\n  detail::format::ranges_specs specs_{};\n};\n\n/**\n * Formatter for contiguous ranges.\n * @tparam T The type.\n */\ntemplate <typename T>\n  requires(detail::format::is_valid_range<T> && detail::format::is_contiguous_but_not_span<T>)\nclass formatter<T> : public formatter<std::span<const detail::format::element_type_t<T>>> {};\n\n/**\n * Formatter for tuple like types.\n * @tparam T The type.\n */\ntemplate <typename T>\n  requires(detail::format::is_valid_tuple<T>)\nclass formatter<T> {\n public:\n  constexpr formatter() : specs_{detail::sv(\"(\"), detail::sv(\")\"), detail::sv(\", \")} {}\n\n  constexpr void set_separator(std::string_view separator) noexcept {\n    specs_.separator = separator;\n  }\n\n  constexpr void set_brackets(std::string_view opening_bracket, std::string_view closing_bracket) noexcept {\n    specs_.opening_bracket = opening_bracket;\n    specs_.closing_bracket = closing_bracket;\n  }\n\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    EMIO_TRY(char c, format_rdr.read_char());\n    if (c == 'n') {\n      EMIO_TRY(c, format_rdr.read_char());\n    }\n    if (c == '}') {\n      return success;\n    }\n    if (c == ':') {\n      return validate_for_each(std::make_index_sequence<std::tuple_size_v<T>>(), format_rdr);\n    } else {\n      return err::invalid_format;\n    }\n  }\n\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    char c = format_rdr.peek().assume_value();\n    if (c == 'n') {\n      set_brackets({}, {});\n      format_rdr.pop();  // n\n      c = format_rdr.peek().assume_value();\n    }\n    bool set_debug = false;\n    if (c == '}') {\n      set_debug = true;\n    } else {\n      format_rdr.pop();  // :\n    }\n    return parse_for_each(std::make_index_sequence<std::tuple_size_v<T>>(), format_rdr, set_debug);\n  }\n\n  constexpr result<void> format(writer& out, const T& args) const noexcept {\n    EMIO_TRYV(out.write_str(specs_.opening_bracket));\n    EMIO_TRYV(format_for_each(std::make_index_sequence<std::tuple_size_v<T>>(), out, args));\n    EMIO_TRYV(out.write_str(specs_.closing_bracket));\n    return success;\n  }\n\n private:\n  template <size_t... Ns>\n  static constexpr result<void> validate_for_each(std::index_sequence<Ns...> /*unused*/, reader& format_rdr) noexcept {\n    size_t reader_pos = 0;\n    result<void> res = success;\n    const auto validate = [&reader_pos, &res](const auto type_identity, reader r /*copy!*/) noexcept {\n      res = decltype(type_identity)::type::validate(r);\n      if (res.has_error()) {\n        return false;\n      }\n      if (reader_pos == 0) {\n        reader_pos = r.pos();\n      } else if (reader_pos != r.pos()) {\n        res = err::invalid_format;\n      }\n      return res.has_value();\n    };\n    static_cast<void>(validate);  // Maybe unused warning.\n    if ((validate(std::type_identity<std::tuple_element_t<Ns, detail::format::tuple_formatters<T>>>{}, format_rdr) &&\n         ...) &&\n        reader_pos != 0) {\n      format_rdr.pop(reader_pos);\n      return success;\n    }\n    return res;\n  }\n\n  static constexpr result<void> validate_for_each(std::index_sequence<> /*unused*/, reader& /*format_rdr*/) noexcept {\n    return err::invalid_format;\n  }\n\n  template <size_t... Ns>\n  constexpr result<void> parse_for_each(std::index_sequence<Ns...> /*unused*/, reader& format_rdr,\n                                        const bool set_debug) noexcept {\n    using std::get;\n\n    size_t reader_pos = 0;\n    result<void> res = success;\n    const auto parse = [&reader_pos, &res, set_debug](auto& f, reader r /*copy!*/) noexcept {\n      detail::format::maybe_set_debug_format(f, set_debug);\n      res = f.parse(r);\n      reader_pos = r.pos();\n      return res.has_value();\n    };\n    static_cast<void>(parse);  // Maybe unused warning.\n    if ((parse(get<Ns>(formatters_), format_rdr) && ...)) {\n      format_rdr.pop(reader_pos);\n      return success;\n    }\n    return res;\n  }\n\n  constexpr result<void> parse_for_each(std::index_sequence<> /*unused*/, reader& format_rdr,\n                                        const bool set_debug) noexcept {\n    if (set_debug) {\n      format_rdr.pop();  // }\n      return success;\n    }\n    return err::invalid_format;\n  }\n\n  template <size_t N, size_t... Ns>\n  constexpr result<void> format_for_each(std::index_sequence<N, Ns...> /*unused*/, writer& out,\n                                         const T& args) const noexcept {\n    using std::get;\n    EMIO_TRYV(get<N>(formatters_).format(out, get<N>(args)));\n\n    result<void> res = success;\n    const auto format = [&res, &out, this](auto& f, const auto& arg) noexcept {\n      res = out.write_str(specs_.separator);\n      if (res.has_error()) {\n        return false;\n      }\n      res = f.format(out, arg);\n      return res.has_value();\n    };\n    static_cast<void>(format);  // Maybe unused warning.\n    if ((format(get<Ns>(formatters_), get<Ns>(args)) && ...)) {\n      return success;\n    }\n    return res;\n  }\n\n  constexpr result<void> format_for_each(std::index_sequence<> /*unused*/, writer& /*out*/,\n                                         const T& /*args*/) const noexcept {\n    return success;\n  }\n\n  detail::format::tuple_formatters<T> formatters_{};\n  detail::format::ranges_specs specs_{};\n};\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/reader.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <algorithm>\n#include <cstring>\n#include <string_view>\n#include <type_traits>\n\n#include \"detail/conversion.hpp\"\n#include \"result.hpp\"\n\nnamespace emio {\n\n// Forward declaration.\nclass reader;\n\nnamespace detail {\n\ninline constexpr const char*& get_it(reader& rdr) noexcept;\n\ninline constexpr const char* get_end(reader& rdr) noexcept;\n\ninline constexpr result<bool> parse_sign(reader& in) noexcept;\n\ntemplate <typename T>\nconstexpr result<T> parse_int(reader& in, int base, bool is_negative) noexcept;\n\n}  // namespace detail\n\n/**\n * This class operates on a char sequence and allows reading and parsing from it.\n * The reader interprets the char sequence as finite input stream. After every successful operation the read position\n * moves on until the last char of the sequence has been consumed.\n */\nclass reader {\n public:\n  /// The size type.\n  using size_type = typename std::string_view::size_type;\n  /// Special value to indicate the end of the view or an error by functions returning an index.\n  static constexpr size_type npos = std::string_view::npos;\n\n  /**\n   * Constructs an empty reader.\n   */\n  constexpr reader() = default;\n\n  // Don't allow temporary strings or any nullptr.\n  constexpr reader(std::nullptr_t) = delete;\n  constexpr reader(int) = delete;\n#if __STDC_HOSTED__\n  constexpr reader(std::string&&) = delete;  // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved): as intended\n#endif\n\n  /**\n   * Constructs the reader from any suitable char sequence.\n   * @param input The char sequence.\n   */\n  template <typename Arg>\n    requires(std::is_constructible_v<std::string_view, Arg> && !std::is_same_v<Arg, std::string_view>)\n  // NOLINTNEXTLINE(bugprone-forwarding-reference-overload): Is guarded by require clause.\n  constexpr explicit reader(Arg&& input) noexcept : reader{std::string_view{std::forward<Arg>(input)}} {}\n\n  /**\n   * Constructs the reader from a string view.\n   * @param sv The string view.\n   */\n  constexpr explicit reader(const std::string_view& sv) noexcept\n      : begin_{detail::begin(sv)}, it_{begin_}, end_{detail::end(sv)} {}\n\n  /**\n   * Returns the current read position.\n   * @return The read position.\n   */\n  [[nodiscard]] constexpr size_t pos() const noexcept {\n    return static_cast<size_t>(it_ - begin_);\n  }\n\n  /**\n   * Checks if the end of the stream has been reached.\n   * @return True if reached and all chars are read, otherwise false.\n   */\n  [[nodiscard]] constexpr bool eof() const noexcept {\n    return end_ == it_;\n  }\n\n  /**\n   * Returns the number of the not yet read chars of the stream.\n   * @return The number of remaining chars.\n   */\n  [[nodiscard]] constexpr size_t cnt_remaining() const noexcept {\n    return static_cast<size_t>(end_ - it_);\n  }\n\n  /**\n   * Obtains a view of the not yet read chars of the stream.\n   * @return The view over the remaining chars.\n   */\n  [[nodiscard]] constexpr std::string_view view_remaining() const noexcept {\n    return {it_, end_};\n  }\n\n  /**\n   * Reads all remaining chars from the stream.\n   * @return The remaining chars. Could be empty.\n   */\n  [[nodiscard]] constexpr std::string_view read_remaining() noexcept {\n    const std::string_view remaining_view = view_remaining();\n    it_ = end_;\n    return remaining_view;\n  }\n\n  /**\n   * Pops one (default) or n chars from the reader.\n   * @note Does never overflow.\n   * @param cnt The number of chars to pop.\n   */\n  constexpr void pop(const size_t cnt = 1) noexcept {\n    if (static_cast<size_t>(end_ - it_) >= cnt) {\n      it_ += cnt;\n    } else {\n      it_ = end_;\n    }\n  }\n\n  /**\n   * Makes one (default) or n chars available again to read.\n   * @note Does never underflow.\n   * @param cnt The number of chars to unpop.\n   */\n  constexpr void unpop(const size_t cnt = 1) noexcept {\n    if (static_cast<size_t>(it_ - begin_) >= cnt) {\n      it_ -= cnt;\n    } else {\n      it_ = begin_;\n    }\n  }\n\n  /**\n   * Returns a newly constructed reader over the not yet read char sequence of the range [pos, pos + len).\n   * If len is greater than the size of the remaining chars, the end of the char sequence is used.\n   * @param pos The position of the first char to include.\n   * @param len The length of the char sequence.\n   * @return EOF if the position is outside the char sequence.\n   */\n  constexpr result<reader> subreader(const size_t pos, const size_t len = npos) const noexcept {\n    const char* const next_it = it_ + pos;\n    if (next_it > end_) {\n      return err::eof;\n    }\n    const size_t rlen = std::min(len, static_cast<size_t>(end_ - next_it));\n    return reader{std::string_view{next_it, rlen}};\n  }\n\n  /**\n   * Returns the next char from the stream without consuming it.\n   * @return EOF if the end of the stream has been reached.\n   */\n  constexpr result<char> peek() const noexcept {\n    if (it_ != end_) {\n      return *it_;\n    }\n    return err::eof;\n  }\n\n  /**\n   * Reads one char from the stream.\n   * @return EOF if the end of the stream has been reached.\n   */\n  constexpr result<char> read_char() noexcept {\n    if (it_ != end_) {\n      return *it_++;\n    }\n    return err::eof;\n  }\n\n  /**\n   * Reads n chars from the stream.\n   * @param n The number of chars to read.\n   * @return EOF if the end of the stream has been reached before reading n chars.\n   */\n  constexpr result<std::string_view> read_n_chars(const size_t n) noexcept {\n    if (static_cast<size_t>(end_ - it_) >= n) {\n      std::string_view res{it_, it_ + n};\n      it_ += n;\n      return res;\n    }\n    return err::eof;\n  }\n\n  /**\n   * Parses an integer from the stream.\n   * @tparam T The type of the integer.\n   * @param base The input base of the integer. Must be greater equal 2 and less equal 36.\n   * @return invalid_argument if the requested input base is not supported, EOF if the end of the stream has been\n   * reached or invalid_data if the char sequence cannot be parsed as integer.\n   */\n  template <typename T>\n    requires(std::is_integral_v<T>)\n  constexpr result<T> parse_int(const int base = 10) noexcept {\n    // Store current read position.\n    const char* const backup_it = it_;\n\n    // Reduce code generation by upcasting the integer.\n    using upcasted_t = detail::upcasted_int_t<T>;\n    const result<upcasted_t> res = parse_sign_and_int<upcasted_t>(base);\n    if (!res) {\n      it_ = backup_it;\n      return res.assume_error();\n    }\n    if constexpr (std::is_same_v<upcasted_t, T>) {\n      return res;\n    } else {\n      // Check if upcast int is within the integer type range.\n      const upcasted_t val = res.assume_value();\n      if (val < std::numeric_limits<T>::min() || val > std::numeric_limits<T>::max()) {\n        it_ = backup_it;\n        return err::out_of_range;\n      }\n      return static_cast<T>(val);\n    }\n  }\n\n  /**\n   * Parse options for read_until operations.\n   */\n  struct read_until_options {\n    bool include_delimiter{false};  ///< If true, the delimiter is part of the output char sequence, otherwise not.\n    bool keep_delimiter{false};     ///< If true, the delimiter will be kept inside the stream, otherwise consumed.\n    bool ignore_eof{false};  ///< If true, reaching EOF does result in a failed read, otherwise in a successfully read.\n  };\n\n  /**\n   * Reads multiple chars from the stream until a given char as delimiter is reached or EOF (configurable).\n   * @param delimiter The char delimiter.\n   * @param options The read until options.\n   * @return invalid_data if the delimiter hasn't been found and ignore_eof is set to true or EOF if the stream is\n   * empty.\n   */\n  constexpr result<std::string_view> read_until_char(\n      const char delimiter, const read_until_options& options = default_read_until_options()) noexcept {\n    return read_until_match(std::find(it_, end_, delimiter), options);\n  }\n\n  /**\n   * Reads multiple chars from the stream until a given char sequence as delimiter is reached or EOF (configurable).\n   * @param delimiter The char sequence.\n   * @param options The read until options.\n   * @return invalid_data if the delimiter hasn't been found and ignore_eof is set to true or EOF if the stream is\n   * empty.\n   */\n  constexpr result<std::string_view> read_until_str(const std::string_view& delimiter,\n                                                    const read_until_options& options = default_read_until_options()) {\n    return read_until_pos(view_remaining().find(delimiter), options, delimiter.size());\n  }\n\n  /**\n   * Reads multiple chars from the stream until a char of a given group is reached or EOF (configurable).\n   * @param group The char group.\n   * @param options The read until options.\n   * @return invalid_data if no char has been found and ignore_eof is set to True or EOF if the stream is empty.\n   */\n  constexpr result<std::string_view> read_until_any_of(\n      const std::string_view& group, const read_until_options& options = default_read_until_options()) noexcept {\n    return read_until_pos(view_remaining().find_first_of(group), options);\n  }\n\n  /**\n   * Reads multiple chars from the stream until no char of a given group is reached or EOF (configurable).\n   * @param group The char group.\n   * @param options The read until options.\n   * @return invalid_data if a char not in the group has been found and ignore_eof is set to True or EOF if the stream\n   * is empty.\n   */\n  constexpr result<std::string_view> read_until_none_of(\n      const std::string_view& group, const read_until_options& options = default_read_until_options()) noexcept {\n    return read_until_pos(view_remaining().find_first_not_of(group), options);\n  }\n\n  /**\n   * Reads multiple chars from the stream until a given predicate returns true or EOF is reached (configurable).\n   * @param predicate The predicate taking a char and returning a bool.\n   * @param options The read until options.\n   * @return invalid_data if the predicate never returns true and ignore_eof is set to True or EOF if the stream is\n   * empty.\n   */\n  template <typename Predicate>\n    requires(std::is_invocable_r_v<bool, Predicate, char>)\n  constexpr result<std::string_view>\n  read_until(const Predicate& predicate, const read_until_options& options = default_read_until_options()) noexcept(\n      std::is_nothrow_invocable_r_v<bool, Predicate, char>) {\n    return read_until_match(std::find_if(it_, end_, predicate), options);\n  }\n\n  /**\n   * Reads one char from the stream if the char matches the expected one.\n   * @param c The expected char.\n   * @return invalid_data if the chars don't match or EOF if the end of the stream has been reached.\n   */\n  constexpr result<char> read_if_match_char(const char c) noexcept {\n    if (it_ != end_) {\n      if (*it_ == c) {\n        ++it_;\n        return c;\n      }\n      return err::invalid_data;\n    }\n    return err::eof;\n  }\n\n  /**\n   * Reads multiple chars from the stream if the chars matches the expected char sequence.\n   * @param sv The expected char sequence.\n   * @return invalid_data if the chars don't match or EOF if the end of the stream has been reached.\n   */\n  constexpr result<std::string_view> read_if_match_str(const std::string_view& sv) noexcept {\n    const size_t n = sv.size();\n    if (static_cast<size_t>(end_ - it_) < n) {\n      return err::eof;\n    }\n    if (detail::equal_n(it_, detail::begin(sv), n)) {\n      const std::string_view res{it_, n};\n      it_ += n;\n      return res;\n    }\n    return err::invalid_data;\n  }\n\n private:\n  friend constexpr const char*& detail::get_it(reader& rdr) noexcept;\n  friend constexpr const char* detail::get_end(reader& rdr) noexcept;\n\n  // Helper function since GCC and Clang complain about \"member initializer for '...' needed within definition of\n  // enclosing class\". Which is a bug.\n  [[nodiscard]] static constexpr read_until_options default_read_until_options() noexcept {\n    return {};\n  }\n\n  template <typename T>\n  constexpr result<T> parse_sign_and_int(const int base) noexcept {\n    EMIO_TRY(const bool is_negative, detail::parse_sign(*this));\n    return detail::parse_int<T>(*this, base, is_negative);\n  }\n\n  constexpr result<std::string_view> read_until_pos(size_t pos, const read_until_options& options,\n                                                    const size_type delimiter_size = 1) noexcept {\n    const char* match = end_;\n    if (pos != npos) {\n      match = it_ + pos;\n    }\n    return read_until_match(match, options, delimiter_size);\n  }\n\n  constexpr result<std::string_view> read_until_match(const char* match, const read_until_options& options,\n                                                      const size_type delimiter_size = 1) noexcept {\n    if (it_ == end_) {\n      return err::eof;\n    }\n    const char* const begin = it_;\n    if (match == end_) {\n      if (!options.ignore_eof) {\n        it_ = end_;\n        return std::string_view{begin, end_};\n      }\n      return err::invalid_data;\n    }\n    it_ = match;\n    if (!options.keep_delimiter) {\n      it_ += delimiter_size;\n    }\n    if (options.include_delimiter) {\n      match += delimiter_size;\n    }\n    return std::string_view{begin, match};\n  }\n\n  const char* begin_{};\n  const char* it_{};\n  const char* end_{};\n};\n\nnamespace detail {\n\ninline constexpr const char*& get_it(reader& rdr) noexcept {\n  return rdr.it_;\n}\n\ninline constexpr const char* get_end(reader& rdr) noexcept {\n  return rdr.end_;\n}\n\ninline constexpr result<bool> parse_sign(reader& in) noexcept {\n  bool is_negative = false;\n  EMIO_TRY(const char c, in.peek());\n  if (c == '+') {\n    in.pop();\n  } else if (c == '-') {\n    is_negative = true;\n    in.pop();\n  }\n  return is_negative;\n}\n\ntemplate <typename T>\nconstexpr result<T> parse_int(reader& in, const int base, const bool is_negative) noexcept {\n  if (!is_valid_number_base(base)) {\n    return err::invalid_argument;\n  }\n\n  EMIO_TRY(const char c, in.read_char());\n  std::optional<int> digit = char_to_digit(c, base);\n  if (!digit) {\n    return err::invalid_data;\n  }\n\n  if constexpr (std::is_unsigned_v<T>) {\n    if (is_negative) {\n      return err::out_of_range;\n    }\n  }\n\n  T value{};\n  T maybe_overflowed_value{};\n  const auto has_next_digit = [&]() noexcept {\n    value = maybe_overflowed_value;\n\n    const result<char> res = in.peek();\n    if (!res) {\n      return false;\n    }\n    digit = detail::char_to_digit(res.value(), base);\n    if (!digit) {\n      return false;\n    }\n    in.pop();\n    maybe_overflowed_value = value * static_cast<T>(base);\n    return true;\n  };\n\n  if constexpr (std::is_signed_v<T>) {\n    if (is_negative) {\n      while (true) {\n        maybe_overflowed_value -= static_cast<T>(*digit);\n        if (maybe_overflowed_value > value) {\n          return err::out_of_range;\n        }\n        if (!has_next_digit()) {\n          return value;\n        }\n      }\n    }\n  }\n  while (true) {\n    maybe_overflowed_value += static_cast<T>(*digit);\n    if (maybe_overflowed_value < value) {\n      return err::out_of_range;\n    }\n    if (!has_next_digit()) {\n      return value;\n    }\n  }\n}\n\n}  // namespace detail\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/result.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <concepts>\n#include <cstdint>\n#include <optional>\n#include <string_view>\n#if __STDC_HOSTED__\n#  include <stdexcept>\n#  include <string>\n#endif\n#include <type_traits>\n\n#include \"detail/predef.hpp\"\n\n/**\n * Evaluates an expression. If successful, continues the execution. If unsuccessful, immediately returns from the\n * calling function.\n * @param expr The expression to evaluate.\n */\n#define EMIO_TRYV(expr) EMIO_Z_INTERNAL_TRYV(EMIO_Z_INTERNAL_UNIQUE_NAME, expr)\n\n/**\n * Evaluates an expression. If successful, assigns the value to a declaration. If unsuccessful, immediately returns from\n * the calling function.\n * @param var The declaration to assign the value to.\n * @param expr The expression to evaluate.\n */\n#define EMIO_TRY(var, expr) EMIO_Z_INTERNAL_TRY(EMIO_Z_INTERNAL_UNIQUE_NAME, var, expr)\n\nnamespace emio {\n\n/**\n * A list of possible I/O errors.\n */\nenum class err {\n  // success = 0,    ///< Internal used for no error.\n  eof = 1,           ///< End of file (e.g. reaching the end of an output array).\n  invalid_argument,  ///< A parameter is incorrect (e.g. the output base is invalid).\n  invalid_data,      ///< The data is malformed (e.g. no digit where a digit was expected).\n  out_of_range,      ///< The parsed value is not in the range representable by the type (e.g. parsing 578 as uint8_t).\n  invalid_format,    ///< The format string is invalid.\n};\n\n/**\n * Returns the name of the possible I/O errors.\n * @param error The error.\n * @return The name.\n */\nconstexpr std::string_view to_string(err error) noexcept {\n  using namespace std::string_view_literals;\n\n  if (error == err{}) {\n    return \"no error\"sv;\n  }\n  switch (error) {\n  case err::eof:\n    return \"eof\"sv;\n  case err::invalid_argument:\n    return \"invalid argument\"sv;\n  case err::invalid_data:\n    return \"invalid data\"sv;\n  case err::out_of_range:\n    return \"out of range\"sv;\n  case err::invalid_format:\n    return \"invalid format\"sv;\n  }\n  std::terminate();\n}\n\n/**\n * Used to indicates a successful operation without any value.\n */\ninline constexpr struct {\n} success;\n\n/**\n * This exception type indicating an incorrect observation of value or error occurred by result<T>.\n *\n * No member functions are added in addition to std::logic_error. Typical .what() strings are:\n * - \"no value\" -> a value should be accessed but result<T> held an error\n * - \"no error\" -> a error should be accessed but result<T> held an value\n */\n#if __STDC_HOSTED__\nclass bad_result_access : public std::logic_error {\n public:\n  /**\n   * Constructs the bad result access from a message.\n   * @param msg The exception message.\n   */\n  explicit bad_result_access(const std::string_view& msg) : logic_error{std::string{msg}} {}\n};\n#else\nclass bad_result_access : public std::exception {\n public:\n  /**\n   * Constructs the bad result access from a message.\n   * @param msg The exception message.\n   */\n  explicit bad_result_access(const std::string_view& msg) : msg_{msg} {}\n\n  [[nodiscard]] const char* what() const noexcept override {\n    return msg_.data();\n  }\n\n private:\n  std::string_view msg_;\n};\n#endif\n\nnamespace detail {\n\n#ifdef __EXCEPTIONS\ninline constexpr bool exceptions_disabled = false;\n#else\ninline constexpr bool exceptions_disabled = true;\n#endif\n\n// Helper function to throw or terminate, depending on whether exceptions are globally enabled or not.\n[[noreturn]] inline void throw_bad_result_access_or_terminate(const err error) noexcept(exceptions_disabled) {\n#ifdef __EXCEPTIONS\n  throw bad_result_access{to_string(error)};\n#else\n  static_cast<void>(error);\n  std::terminate();\n#endif\n}\n\n}  // namespace detail\n\n/**\n * This class provides a way to store an optional value after an successful operation or an error after an failed\n * operation. The error type is emio::err.\n * @note See boost::outcome, std::expected or rust's std::result for the basic idea.\n * This provided API is a mix of each world.\n * @tparam T The value type to hold on success.\n */\ntemplate <typename Value>\nclass [[nodiscard]] result;\n\n/**\n * Partial template specification of result<T> for any non-reference type.\n * @tparam Value The value type to hold on success.\n */\ntemplate <typename Value>\n  requires(!std::is_reference_v<Value>)\nclass [[nodiscard]] result<Value> {\n public:\n  /**\n   * Explicit not default constructable.\n   */\n  constexpr result() = delete;\n\n  /**\n   * Constructs a result from a value to indicate an success.\n   * @param value The value.\n   */\n  template <typename U>\n    requires(std::is_constructible_v<Value, U>)\n  // NOLINTNEXTLINE(bugprone-forwarding-reference-overload): Is guarded by require clause.\n  constexpr explicit(!std::is_convertible_v<U, Value>) result(U&& value) : value_{std::forward<U>(value)} {}\n\n  /**\n   * Constructs a result from an error to indicate a failure.\n   * @param error The error.\n   */\n  constexpr result(err error) : error_{error} {\n    EMIO_Z_DEV_ASSERT(error != err{});\n  }\n\n  /**\n   * Checks whether the object holds a value.\n   * @return True if it holds a value, otherwise false.\n   */\n  constexpr explicit operator bool() const noexcept {\n    return has_value();\n  }\n\n  /**\n   * Checks whether the object holds a value.\n   * @return True if it holds a value, otherwise false.\n   */\n  [[nodiscard]] constexpr bool has_value() const noexcept {\n    return value_.has_value();\n  }\n\n  /**\n   * Checks whether the object holds an error.\n   * @return True if it holds an error, otherwise false.\n   */\n  [[nodiscard]] constexpr bool has_error() const noexcept {\n    return !has_value();\n  }\n\n  /**\n   * Returns a pointer to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  constexpr std::remove_reference_t<Value>* operator->() noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return &*value_;  // NOLINT(bugprone-unchecked-optional-access): assumed\n  }\n\n  /**\n   * Returns a const pointer to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  constexpr const std::remove_reference_t<Value>* operator->() const noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return &*value_;  // NOLINT(bugprone-unchecked-optional-access): assumed\n  }\n\n  /**\n   * Returns a reference to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  [[nodiscard]] constexpr Value& operator*() & noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return *value_;\n  }\n\n  /**\n   * Returns a const reference to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  [[nodiscard]] constexpr const Value& operator*() const& noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return *value_;\n  }\n\n  /**\n   * Returns a rvalue reference to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  [[nodiscard]] constexpr Value&& operator*() && noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return std::move(*value_);\n  }\n\n  /**\n   * Returns a const rvalue reference to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  [[nodiscard]] constexpr const Value&& operator*() const&& noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return std::move(*value_);\n  }\n\n  /**\n   * Returns a reference to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  [[nodiscard]] constexpr Value& assume_value() & noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return *value_;  // NOLINT(bugprone-unchecked-optional-access): assumed\n  }\n\n  /**\n   * Returns a const reference to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  [[nodiscard]] constexpr const Value& assume_value() const& noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return *value_;  // NOLINT(bugprone-unchecked-optional-access): assumed\n  }\n\n  /**\n   * Returns a rvalue reference to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  [[nodiscard]] constexpr Value&& assume_value() && noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return std::move(*value_);  // NOLINT(bugprone-unchecked-optional-access): assumed\n  }\n\n  /**\n   * Returns a const rvalue reference to the value.\n   * @note The behavior is undefined if this->has_value() is false.\n   * @return The value.\n   */\n  [[nodiscard]] constexpr const Value&& assume_value() const&& noexcept {\n    EMIO_Z_DEV_ASSERT(has_value());\n    return std::move(*value_);  // NOLINT(bugprone-unchecked-optional-access): assumed\n  }\n\n  /**\n   * Returns a reference to the value.\n   * @return The value.\n   */\n  constexpr Value& value() & noexcept(detail::exceptions_disabled) {\n    if (value_.has_value()) [[likely]] {\n      return *value_;\n    }\n    detail::throw_bad_result_access_or_terminate(error_);\n  }\n\n  /**\n   * Returns a const reference to the value.\n   * @return The value.\n   */\n  [[nodiscard]] constexpr const Value& value() const& noexcept(detail::exceptions_disabled) {\n    if (value_.has_value()) [[likely]] {\n      return *value_;\n    }\n    detail::throw_bad_result_access_or_terminate(error_);\n  }\n\n  /**\n   * Returns a rvalue reference to the value.\n   * @return The value.\n   */\n  constexpr Value&& value() && noexcept(detail::exceptions_disabled) {\n    if (value_.has_value()) [[likely]] {\n      return std::move(*value_);\n    }\n    detail::throw_bad_result_access_or_terminate(error_);\n  }\n\n  /**\n   * Returns a const rvalue reference to the value.\n   * @return The value.\n   */\n  // NOLINTNEXTLINE(modernize-use-nodiscard): access check\n  constexpr const Value&& value() const&& noexcept(detail::exceptions_disabled) {\n    if (value_.has_value()) [[likely]] {\n      return std::move(*value_);\n    }\n    detail::throw_bad_result_access_or_terminate(error_);\n  }\n\n  /**\n   * Returns the error.\n   * @note The behavior is undefined if this->has_error() is false.\n   * @return The error.\n   */\n  [[nodiscard]] constexpr err assume_error() const noexcept {\n    EMIO_Z_DEV_ASSERT(!has_value());\n    return error_;\n  }\n\n  /**\n   * Returns the error.\n   * @return The error.\n   * @throws bad_result_access exception or terminates (if exceptions are disabled) this->has_error() is false.\n   */\n  constexpr err error() const noexcept(detail::exceptions_disabled) {  // NOLINT(modernize-use-nodiscard): access check\n    if (has_error()) [[likely]] {\n      return error_;\n    }\n    detail::throw_bad_result_access_or_terminate(error_);\n    return {};  // afl-c++ requires a return statement.\n  }\n\n  /**\n   * Returns the contained value if *this has a value, otherwise returns default_value.\n   * @param default_value The value to use in case *this is empty.\n   * @return The current value if *this has a value, or default_value otherwise.\n   */\n  template <typename U>\n  constexpr Value value_or(U&& default_value) const& {\n    return value_.value_or(std::forward<U>(default_value));\n  }\n\n  /**\n   * Returns the contained value if *this has a value, otherwise returns default_value.\n   * @param default_value The value to use in case *this is empty.\n   * @return The current value if *this has a value, or default_value otherwise.\n   */\n  template <typename U>\n  constexpr Value value_or(U&& default_value) && {\n    return value_.value_or(std::forward<U>(default_value));\n  }\n\n private:\n  std::optional<Value> value_{};\n  err error_{};\n};\n\n/**\n * Partial template specification of result<T> for no type (void).\n * @note Although result<void> does not hold any value, the functions like has_value, assume_value and value are\n * provided with the exact same name to keep the API uniform.\n */\ntemplate <>\nclass [[nodiscard]] result<void> {\n public:\n  /**\n   * Explicit not default constructable.\n   */\n  constexpr result() = delete;\n\n  /**\n   * Constructs a result from the success value to indicate an success.\n   * @param s The success value.\n   */\n  constexpr result([[maybe_unused]] decltype(success) s) {}\n\n  /**\n   * Constructs a result from an error to indicate a failure.\n   * @param error The error.\n   */\n  constexpr result(err error) : error_{error} {\n    EMIO_Z_DEV_ASSERT(error != err{});\n  }\n\n  /**\n   * Constructs a result from another non void result and copies the success or error state.\n   * @param other The other result.\n   */\n  template <typename T>\n    requires(!std::is_void_v<T>)\n  constexpr result(const result<T>& other) {\n    if (other.has_error()) {\n      error_ = other.assume_error();\n    }\n  }\n\n  /**\n   * Checks whether the object holds a value.\n   * @return True if it holds a value, otherwise false.\n   */\n  constexpr explicit operator bool() const noexcept {\n    return has_value();\n  }\n\n  /**\n   * Checks whether the object holds a value.\n   * @return True if it holds a value, otherwise false.\n   */\n  [[nodiscard]] constexpr bool has_value() const noexcept {\n    return error_ == err{};\n  }\n\n  /**\n   * Checks whether the object holds an error.\n   * @return True if it holds an error, otherwise false.\n   */\n  [[nodiscard]] constexpr bool has_error() const noexcept {\n    return !has_value();\n  }\n\n  /**\n   * Does nothing for this type. Kept for consistent API.\n   */\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static): Kept non-static for consistent API.\n  constexpr void assume_value() const noexcept {\n    // Nothing.\n    EMIO_Z_DEV_ASSERT(has_value());\n  }\n\n  /**\n   * If the result does have an error, this function throws/terminates.\n   * @throws bad_result_access exception or terminates (if exceptions are disabled) if this->has_value() is false.\n   */\n  constexpr void value() const noexcept(detail::exceptions_disabled) {\n    if (!has_value()) [[unlikely]] {\n      detail::throw_bad_result_access_or_terminate(error_);\n    }\n  }\n\n  /**\n   * Returns the error.\n   * @note The behavior is undefined if this->has_error() is false.\n   * @return The error.\n   */\n  [[nodiscard]] constexpr err assume_error() const noexcept {\n    EMIO_Z_DEV_ASSERT(!has_value());\n    return error_;\n  }\n\n  /**\n   * Returns the error.\n   * @return The error.\n   * @throws bad_result_access exception or terminates (if exceptions are disabled) this->has_error() is false.\n   */\n  constexpr err error() const noexcept(detail::exceptions_disabled) {  // NOLINT(modernize-use-nodiscard): access check\n    if (has_error()) [[likely]] {\n      return error_;\n    }\n    detail::throw_bad_result_access_or_terminate(error_);\n    return {};  // afl-c++ requires a return statement.\n  }\n\n private:\n  err error_{};\n};\n\nnamespace detail {\n\ntemplate <typename T>\nstruct is_result : std::false_type {};\n\ntemplate <typename T>\nstruct is_result<result<T>> : std::true_type {};\n\ntemplate <typename T>\ninline constexpr bool is_result_v = is_result<T>::value;\n\n}  // namespace detail\n\n/**\n * Compares two result objects of maybe different but equality comparable types against each other.\n * @param left The left one.\n * @param right The right one.\n * @details Comparison against inequality will be automatically generated.\n * @return True if equal, otherwise false.\n * They are equal if:\n * - left.has_value() && right.has_value() && left.value() == right.value()\n * - left.has_error() && right.has_error() && left.error() == right.error()\n */\ntemplate <typename T, typename U>\n  requires((std::is_void_v<T> && std::is_void_v<U>) || std::equality_comparable_with<T, U>)\nconstexpr bool operator==(const result<T>& left, const result<U>& right) noexcept {\n  if (left.has_value() != right.has_value()) {\n    return false;\n  }\n  if (left.has_value()) {\n    if constexpr (std::is_void_v<T> && std::is_void_v<U>) {\n      return true;\n    } else {\n      return left.assume_value() == right.assume_value();\n    }\n  }\n  return left.assume_error() == right.assume_error();\n}\n\n/**\n * Compares a result object with an object of maybe different but equality comparable type against each other.\n * @param left The left one.\n * @param right The right one.\n * @details Comparison against inequality or right ==/!= left will be automatically generated.\n * @return True if equal, otherwise false.\n * They are equal if:\n * - left.has_value() && left.value() == right\n */\ntemplate <typename T, typename U>\n  requires(!detail::is_result_v<U> && std::equality_comparable_with<T, U>)\nconstexpr bool operator==(const result<T>& left, const U& right) noexcept {\n  if (left.has_value()) {\n    return left.value() == right;\n  }\n  return false;\n}\n\n/**\n * Compares a result object against an error value for equality.\n * @param left The left one.\n * @param right The right one.\n * @details Comparison against inequality or right ==/!= left will be automatically generated.\n * @return True if equal, otherwise false.\n * They are equal if:\n * - left.has_error() && left.error() == right\n */\ntemplate <typename T>\nconstexpr bool operator==(const result<T>& left, const err right) noexcept {\n  return left.has_error() && left.error() == right;\n}\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/scan.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"detail/scan/scan_from.hpp\"\n\nnamespace emio {\n\n/**\n * Provides access to the format string and the arguments to scan.\n * @note This type should only be \"constructed\" via make_scan_args(format_str, args...) and passed directly to a\n * scanning function.\n */\nusing scan_args = detail::scan::scan_args;\n\n// Alias template types.\ntemplate <typename... Args>\nusing format_scan_string = detail::scan::format_string<Args...>;\n\ntemplate <typename... Args>\nusing valid_format_scan_string = detail::scan::valid_format_string<Args...>;\n\n/**\n * Returns an object that stores a format string with an array of all arguments to scan.\n *\n * @note The storage uses reference semantics and does not extend the lifetime of args. It is the programmer's\n * responsibility to ensure that args outlive the return value. Usually, the result is only used as argument to a\n * scanning function taking scan_args by reference.\n *\n * @param format_str The format string.\n * @param args The arguments to be scanned.\n * @return Internal type. Implicit convertible to scan_args.\n */\ntemplate <typename... Args>\n[[nodiscard]] detail::args_storage<detail::scan::scan_arg, sizeof...(Args)> make_scan_args(\n    format_scan_string<Args...> format_str, Args&... args) noexcept {\n  return {format_str, args...};\n}\n\n/**\n * Scans the content of the reader for the given arguments according to the format string.\n * @param in_rdr The reader to scan.\n * @param args The scan args with format string.\n * @return Success if the scanning was successfully for all arguments. The reader may not be empty.\n */\ninline result<void> vscan_from(reader& in_rdr, const scan_args& args) noexcept {\n  return detail::scan::vscan_from(in_rdr, args);\n}\n\n/**\n * Scans the content of the input string for the given arguments according to the format string.\n * @param in The input string to scan.\n * @param args The scan args with format string.\n * @return Success if the scanning was successfully for all arguments for the entire input string.\n */\ninline result<void> vscan(std::string_view in, const scan_args& args) noexcept {\n  reader in_rdr{in};\n  EMIO_TRYV(detail::scan::vscan_from(in_rdr, args));\n  if (in_rdr.eof()) {\n    return success;\n  }\n  return err::invalid_format;\n}\n\n/**\n * Scans the content of the reader for the given arguments according to the format string.\n * @param in_rdr The reader to scan.\n * @param format_str The format string.\n * @param args The arguments which are to be scanned.\n * @return Success if the scanning was successfully for all arguments. The reader may not be empty.\n */\ntemplate <typename... Args>\nconstexpr result<void> scan_from(reader& in_rdr, format_scan_string<Args...> format_str, Args&... args) noexcept {\n  if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {\n    EMIO_TRYV(detail::scan::scan_from(in_rdr, format_str, args...));\n  } else {\n    EMIO_TRYV(detail::scan::vscan_from(in_rdr, make_scan_args(format_str, args...)));\n  }\n  return success;\n}\n\n/**\n * Scans the input string for the given arguments according to the format string.\n * @param input The input string.\n * @param format_str The format string.\n * @param args The arguments which are to be scanned.\n * @return Success if the scanning was successfully for all arguments for the entire input string.\n */\ntemplate <typename... Args>\nconstexpr result<void> scan(std::string_view input, format_scan_string<Args...> format_str, Args&... args) noexcept {\n  reader rdr{input};\n  EMIO_TRYV(emio::scan_from(rdr, format_str, args...));\n  if (rdr.eof()) {\n    return success;\n  }\n  return err::invalid_format;\n}\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/scanner.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include \"detail/misc.hpp\"\n#include \"detail/scan/scanner.hpp\"\n\nnamespace emio {\n\n/**\n * Checks if a type is scannable.\n * @tparam T The type to check.\n */\ntemplate <typename T>\ninline constexpr bool is_scannable_v = detail::scan::has_scanner_v<std::remove_cvref_t<T>>;\n\n/**\n * Class template that defines scanning rules for a given type.\n * @note This class definition is just a mock-up. See other template specialization for a concrete scanning.\n * @tparam T The type to scan.\n */\ntemplate <typename T>\nclass scanner {\n public:\n  // Not constructable because this is just a minimal example how to write a custom scanner.\n  scanner() = delete;\n\n  /**\n   * Optional static function to validate the format string syntax for this type.\n   * @note If not present, the parse function is invoked for validation.\n   * @param format_rdr The reader over the format string.\n   * @return Success if the format string is valid.\n   */\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  /**\n   * Function to parse the format specs for this type.\n   * @param format_rdr The reader over the format string.\n   * @return Success if the format string is valid and could be parsed.\n   */\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  /**\n   * Function to scan the object of this type according to the parsed format specs.\n   * @param input The input reader.\n   * @param arg The argument to scan.\n   * @return Success if the scanning could be done.\n   */\n  constexpr result<void> scan(reader& in, T& arg) const noexcept {\n    EMIO_TRY(arg, in.parse_int<T>());\n    return success;\n  }\n};\n\n/**\n * Scanner for most common unambiguity types.\n * This includes:\n * - char\n * - integral\n * To be implemented:\n * - floating-point types\n * @tparam T The type.\n */\ntemplate <typename T>\n  requires(detail::scan::is_core_type_v<T>)\nclass scanner<T> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    detail::scan::format_specs specs{};\n    EMIO_TRYV(detail::scan::validate_format_specs(format_rdr, specs));\n    if constexpr (std::is_same_v<T, char>) {\n      EMIO_TRYV(check_char_specs(specs));\n    } else if constexpr (std::is_integral_v<T>) {\n      EMIO_TRYV(check_integral_specs(specs));\n    } else {\n      static_assert(detail::always_false_v<T>, \"Unknown core type!\");\n    }\n    return success;\n  }\n\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return detail::scan::parse_format_specs(format_rdr, specs_);\n  }\n\n  constexpr result<void> scan(reader& in, T& arg) const noexcept {\n    return read_arg(in, specs_, arg);\n  }\n\n private:\n  detail::scan::format_specs specs_{};\n};\n\n/**\n * Scanner for integral types which are not core types.\n */\ntemplate <typename T>\n  requires(std::is_integral_v<T> && !std::is_same_v<T, bool> && !detail::scan::is_core_type_v<T>)\nclass scanner<T> : public scanner<detail::upcasted_int_t<T>> {\n private:\n  using upcasted_t = detail::upcasted_int_t<T>;\n\n public:\n  constexpr result<void> scan(reader& in, T& arg) noexcept {\n    upcasted_t val{};\n    EMIO_TRYV(scanner<upcasted_t>::scan(in, val));\n    if (val < std::numeric_limits<T>::min() || val > std::numeric_limits<T>::max()) {\n      return err::out_of_range;\n    }\n    arg = static_cast<T>(val);\n    return success;\n  }\n};\n\n/**\n * Scanner for std::string_view.\n */\ntemplate <>\nclass scanner<std::string_view> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    detail::scan::format_specs specs{};\n    EMIO_TRYV(detail::scan::validate_format_specs(format_rdr, specs));\n    EMIO_TRYV(detail::scan::check_string_specs(specs));\n    return success;\n  }\n\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    EMIO_TRYV(detail::scan::parse_format_specs(format_rdr, specs_));\n    format_rdr_ = format_rdr;\n    return success;\n  }\n\n  constexpr result<void> scan(reader& in, std::string_view& arg) noexcept {\n    return detail::scan::read_string(in, specs_, format_rdr_, arg);\n  }\n\n private:\n  detail::scan::format_specs specs_;\n  reader format_rdr_;\n};\n\n#if __STDC_HOSTED__\n/**\n * Scanner for std::string.\n */\ntemplate <>\nclass scanner<std::string> : public scanner<std::string_view> {\n public:\n  constexpr result<void> scan(reader& in, std::string& arg) noexcept {\n    std::string_view s;\n    EMIO_TRYV(scanner<std::string_view>::scan(in, s));\n    arg = s;\n    return success;\n  }\n};\n#endif\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/std.hpp",
    "content": "//\n// Copyright (c) 2024 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n// Include first.\n#include <version>\n\n// Other.\n#include <exception>\n#if defined(__cpp_lib_expected)\n#  include <expected>\n#endif\n#if __STDC_HOSTED__\n#  include <filesystem>\n#endif\n#include <optional>\n#include <variant>\n\n#include \"formatter.hpp\"\n\nnamespace emio {\n\n/**\n * Formatter for std::optional.\n * @tparam T The type to format.\n */\ntemplate <typename T>\n  requires is_formattable_v<T>\nclass formatter<std::optional<T>> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return detail::format::validate_trait<T>(format_rdr);\n  }\n\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return underlying_.parse(format_rdr);\n  }\n\n  constexpr result<void> format(writer& out, const std::optional<T>& arg) const noexcept {\n    if (!arg.has_value()) {\n      return out.write_str(detail::sv(\"none\"));\n    } else {\n      EMIO_TRYV(out.write_str(detail::sv(\"optional(\")));\n      EMIO_TRYV(underlying_.format(out, *arg));\n      return out.write_char(')');\n    }\n  }\n\n private:\n  formatter<T> underlying_;\n};\n\n/**\n * Formatter for std::exception.\n * @tparam T The type.\n */\ntemplate <typename T>\n  requires std::is_base_of_v<std::exception, T>\nclass formatter<T> : public formatter<std::string_view> {\n public:\n  result<void> format(writer& out, const std::exception& arg) const noexcept {\n    return formatter<std::string_view>::format(out, arg.what());\n  }\n};\n\n#if __STDC_HOSTED__\n/**\n * Formatter for std::filesystem::path.\n */\ntemplate <>\nclass formatter<std::filesystem::path> : public formatter<std::string_view> {\n public:\n  result<void> format(writer& out, const std::filesystem::path& arg) const noexcept {\n    return formatter<std::string_view>::format(out, arg.native());\n  }\n};\n#endif\n\n/**\n * Formatter for std::monostate.\n */\ntemplate <>\nclass formatter<std::monostate> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  static constexpr result<void> parse(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  static constexpr result<void> format(writer& out, const std::monostate& /*arg*/) noexcept {\n    return out.write_str(detail::sv(\"monostate\"));\n  }\n};\n\nnamespace detail {\n\ntemplate <typename T>\nconstexpr result<void> format_escaped_alternative(writer& out, const T& val) noexcept {\n  if constexpr (std::is_same_v<T, char>) {\n    return out.write_char_escaped(val);\n  } else if constexpr (std::is_same_v<T, std::string_view>) {\n    return out.write_str_escaped(val);\n  } else if constexpr (!std::is_null_pointer_v<T> && std::is_convertible_v<T, std::string_view>) {\n    return out.write_str_escaped(std::string_view{val});\n  } else {\n    formatter<T> fmt;\n    emio::reader rdr{detail::sv(\"}\")};\n    EMIO_TRYV(fmt.parse(rdr));\n    return fmt.format(out, val);\n  }\n}\n\n}  // namespace detail\n\n/**\n * Formatter for std::variant.\n * @tparam Ts The types to format.\n */\ntemplate <typename... Ts>\n  requires(is_formattable_v<Ts> && ...)\nclass formatter<std::variant<Ts...>> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  constexpr result<void> format(writer& out, const std::variant<Ts...>& arg) noexcept {\n    EMIO_TRYV(out.write_str(detail::sv(\"variant(\")));\n#ifdef __EXCEPTIONS\n    try {\n#endif\n      EMIO_TRYV(std::visit(\n          [&out](const auto& val) -> result<void> {\n            return detail::format_escaped_alternative(out, val);\n          },\n          arg));\n#ifdef __EXCEPTIONS\n    } catch (const std::bad_variant_access&) {\n      EMIO_TRYV(out.write_str(detail::sv(\"valueless by exception\")));\n    }\n#endif\n    return out.write_char(')');\n  }\n};\n\n#if defined(__cpp_lib_expected)\n/**\n * Formatter for std::expected.\n * @tparam T The value type.\n * @tparam E The error type.\n */\ntemplate <typename T, typename E>\n  requires(((!std::is_void_v<T> && is_formattable_v<T>) || std::is_void_v<T>) && is_formattable_v<E>)\nclass formatter<std::expected<T, E>> {\n public:\n  static constexpr result<void> validate(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  constexpr result<void> parse(reader& format_rdr) noexcept {\n    return format_rdr.read_if_match_char('}');\n  }\n\n  constexpr result<void> format(writer& out, const std::expected<T, E>& arg) const noexcept {\n    if (arg.has_value()) {\n      EMIO_TRYV(out.write_str(detail::sv(\"expected(\")));\n      if constexpr (!std::is_void_v<T>) {\n        EMIO_TRYV(detail::format_escaped_alternative(out, arg.value()));\n      }\n    } else {\n      EMIO_TRYV(out.write_str(detail::sv(\"unexpected(\")));\n      EMIO_TRYV(detail::format_escaped_alternative(out, arg.error()));\n    }\n    return out.write_char(')');\n  }\n};\n#endif\n\n}  // namespace emio\n"
  },
  {
    "path": "include/emio/writer.hpp",
    "content": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio.hpp\n\n#pragma once\n\n#include <algorithm>\n\n#include \"buffer.hpp\"\n#include \"detail/conversion.hpp\"\n#include \"detail/utf.hpp\"\n\nnamespace emio {\n\n/**\n * This class operates on a buffer and allows writing sequences of characters or other kinds of data into it.\n */\nclass writer {\n public:\n  /**\n   * Constructs a writer with a given buffer.\n   * @param buf The buffer.\n   */\n  constexpr writer(buffer& buf) noexcept : buf_{buf} {}\n\n  /**\n   * Returns the buffer.\n   * @return The buffer.\n   */\n  [[nodiscard]] constexpr buffer& get_buffer() noexcept {\n    return buf_;\n  }\n\n  /**\n   * Writes a character into the buffer.\n   * @param c The character.\n   * @return EOF if the buffer is to small.\n   */\n  constexpr result<void> write_char(const char c) noexcept {\n    EMIO_TRY(const auto area, buf_.get_write_area_of(1));\n    area[0] = c;\n    return success;\n  }\n\n  /**\n   * Writes a character n times into the buffer.\n   * @param c The character.\n   * @param n The number of times the character should be written.\n   * @return EOF if the buffer is to small.\n   */\n  constexpr result<void> write_char_n(const char c, const size_t n) noexcept {\n    // Perform write in multiple chunks, to support buffers with an internal cache.\n    size_t remaining_size = n;\n    while (remaining_size != 0) {\n      EMIO_TRY(const auto area, buf_.get_write_area_of_max(remaining_size));\n      detail::fill_n(area.data(), area.size(), c);\n      remaining_size -= area.size();\n    }\n    return success;\n  }\n\n  /**\n   * Writes a character escaped into the buffer.\n   * @param c The character.\n   * @return EOF if the buffer is to small.\n   */\n  constexpr result<void> write_char_escaped(const char c) noexcept {\n    const std::string_view sv(&c, 1);\n    return detail::write_str_escaped(buf_, sv, detail::count_size_when_escaped(sv), '\\'');\n  }\n\n  /**\n   * Writes a char sequence into the buffer.\n   * @param sv The char sequence.\n   * @return EOF if the buffer is to small.\n   */\n  constexpr result<void> write_str(const std::string_view sv) noexcept {\n    // Perform write in multiple chunks, to support buffers with an internal cache.\n    const char* ptr = sv.data();\n    size_t remaining_size = sv.size();\n    while (remaining_size != 0) {\n      EMIO_TRY(const auto area, buf_.get_write_area_of_max(remaining_size));\n      detail::copy_n(ptr, area.size(), area.data());\n      remaining_size -= area.size();\n      ptr += area.size();\n    }\n    return success;\n  }\n\n  /**\n   * Writes a char sequence escaped into the buffer.\n   * @param sv The char sequence.\n   * @return EOF if the buffer is to small.\n   */\n  constexpr result<void> write_str_escaped(const std::string_view sv) noexcept {\n    return detail::write_str_escaped(buf_, sv, detail::count_size_when_escaped(sv), '\"');\n  }\n\n  /**\n   * Format options for writing integers.\n   */\n  struct write_int_options {\n    int base{10};            ///< The output base of the integer. Must be greater equal 2 and less equal 36.\n    bool upper_case{false};  ///< If true, the letters are upper case, otherwise lower case.\n  };\n\n  /**\n   * Writes an integer into the buffer.\n   * @param integer The integer.\n   * @param options The integer options.\n   * @return invalid_argument if the requested output base is not supported or EOF if the buffer is to small.\n   */\n  template <typename T>\n    requires(std::is_integral_v<T>)\n  constexpr result<void> write_int(const T integer,\n                                   const write_int_options& options = default_write_int_options()) noexcept {\n    // Reduce code generation by upcasting the integer.\n    return write_int_impl(detail::integer_upcast(integer), options);\n  }\n\n private:\n  // Helper function since GCC and Clang complain about \"member initializer for '...' needed within definition of\n  // enclosing class\". Which is a bug.\n  static constexpr write_int_options default_write_int_options() noexcept {\n    return {};\n  }\n\n  template <typename T>\n    requires(std::is_integral_v<T>)\n  constexpr result<void> write_int_impl(const T integer, const write_int_options& options) noexcept {\n    if (!detail::is_valid_number_base(options.base)) {\n      return err::invalid_argument;\n    }\n    const auto abs_number = detail::to_absolute(integer);\n    const bool negative = detail::is_negative(integer);\n    const size_t number_of_digits =\n        detail::get_number_of_digits(abs_number, options.base) + static_cast<size_t>(negative);\n\n    EMIO_TRY(const auto area, buf_.get_write_area_of(number_of_digits));\n    if (negative) {\n      area[0] = '-';\n    }\n    detail::write_number(abs_number, options.base, options.upper_case,\n                         area.data() + detail::to_signed(number_of_digits));\n    return success;\n  }\n\n  buffer& buf_;\n};\n\n}  // namespace emio\n"
  },
  {
    "path": "test/benchmark/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioBenchmarks LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/project-is-top-level.cmake)\ninclude(${CMAKE_SOURCE_DIR}/cmake/folders.cmake)\n\nif (PROJECT_IS_TOP_LEVEL)\n    find_package(emio REQUIRED)\n    enable_testing()\nendif ()\n\nadd_executable(emio_benchmark\n        bench_format.cpp\n        bench_scan.cpp\n        )\n\ntarget_link_libraries(emio_benchmark\n        Catch2::Catch2WithMain\n        emio::emio\n        fmt::fmt\n        )\n\ntarget_compile_features(emio_benchmark PRIVATE cxx_std_20)\n\nadd_test(NAME emio_benchmark COMMAND emio_benchmark)\n\nadd_folders(Benchmark)\n"
  },
  {
    "path": "test/benchmark/bench_format.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <fmt/format.h>\n\n#include <catch2/benchmark/catch_benchmark.hpp>\n#include <catch2/catch_test_macros.hpp>\n#include <cinttypes>\n#include <cmath>\n\nstatic constexpr std::string_view long_text{\n    \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore \"\n    \"magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \"\n    \"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \"\n    \"pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est \"\n    \"laborum.\"};\n\nTEST_CASE(\"format default\") {\n  static constexpr std::string_view format_str{\"Hello world! Let's count together until {}!\"};\n  static constexpr int arg = 42;\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format(format_str, arg);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format(emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format(format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format(fmt::runtime(format_str), arg);\n  };\n}\n\nTEST_CASE(\"format nothing but long text\") {\n  static constexpr std::string_view format_str{long_text};\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str);\n    const std::string fmt_str = fmt::format(format_str);\n    REQUIRE(emio_str == fmt_str);\n\n    REQUIRE(snprintf(buf.data(), buf.size(), format_str.data()) == static_cast<int>(emio_str.size()));\n    REQUIRE(emio_str == buf.data());\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str)).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str));\n  };\n  BENCHMARK(\"snprintf\") {\n    return snprintf(buf.data(), buf.size(), format_str.data());\n  };\n}\n\nTEST_CASE(\"format string\") {\n  static constexpr std::string_view format_str{\" {}\"};\n  static constexpr std::string_view arg = long_text;\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, arg);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, arg);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n\n    REQUIRE(snprintf(buf.data(), buf.size(), \" %*s\", static_cast<int>(arg.size()), arg.data()) ==\n            static_cast<int>(emio_str.size()));\n    REQUIRE(emio_str == buf.data());\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, arg).value();\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), arg);\n  };\n  BENCHMARK(\"snprintf\") {\n    return snprintf(buf.data(), buf.size(), \" %*s\", static_cast<int>(arg.size()), arg.data());\n  };\n}\n\nTEST_CASE(\"format small integer\") {\n  static constexpr std::string_view format_str{\" {}\"};\n  static constexpr int arg = 1;\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, arg);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, arg);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n\n    REQUIRE(snprintf(buf.data(), buf.size(), \" %d\", arg) == static_cast<int>(emio_str.size()));\n    REQUIRE(emio_str == buf.data());\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), arg);\n  };\n  BENCHMARK(\"snprintf\") {\n    return snprintf(buf.data(), buf.size(), \" %d\", arg);\n  };\n}\n\nTEST_CASE(\"format big integer\") {\n  static constexpr std::string_view format_str{\" {}\"};\n  static constexpr int64_t arg = -8978612134175239201;\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, arg);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, arg);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n\n    REQUIRE(snprintf(buf.data(), buf.size(), \" %\" PRIi64, arg) == static_cast<int>(emio_str.size()));\n    REQUIRE(emio_str == buf.data());\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), arg);\n  };\n  BENCHMARK(\"snprintf\") {\n    return snprintf(buf.data(), buf.size(), \" %\" PRIi64, arg);\n  };\n}\n\nTEST_CASE(\"format big hex\") {\n  static constexpr std::string_view format_str{\"{:x}\"};\n  static constexpr uint64_t arg = 8978612134175239201;\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, arg);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, arg);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n\n    REQUIRE(snprintf(buf.data(), buf.size(), \"%\" PRIx64, arg) == static_cast<int>(emio_str.size()));\n    REQUIRE(emio_str == buf.data());\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), arg);\n  };\n  BENCHMARK(\"snprintf\") {\n    return snprintf(buf.data(), buf.size(), \"%\" PRIx64, arg);\n  };\n}\n\nTEST_CASE(\"format complex format spec\") {\n  static constexpr std::string_view format_str{\"{0:x^+#20X}\"};\n  static constexpr int64_t arg = 8978612134175239201;\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, arg);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, arg);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), arg);\n  };\n  // No snprintf equivalent.\n}\n\nTEST_CASE(\"format zero as double\") {\n  static constexpr std::string_view format_str{\"{}\"};\n  static constexpr double arg = 0;\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, arg);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, arg);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n\n    REQUIRE(snprintf(buf.data(), buf.size(), \"%g\", arg) == static_cast<int>(emio_str.size()));\n    REQUIRE(emio_str == buf.data());\n\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), arg);\n  };\n  BENCHMARK(\"snprintf\") {\n    return snprintf(buf.data(), buf.size(), \"%g\", arg);\n  };\n}\n\nTEST_CASE(\"format shortest double general\") {\n  static constexpr std::string_view format_str{\"{}\"};\n  static constexpr double arg = M_PI;\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, arg);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, arg);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), arg);\n  };\n}\n\nTEST_CASE(\"format double exponent\") {\n  static constexpr std::string_view format_str{\"{:e}\"};\n  static constexpr double arg = M_PI;\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, arg);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, arg);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n\n    REQUIRE(snprintf(buf.data(), buf.size(), \"%e\", arg) == static_cast<int>(emio_str.size()));\n    REQUIRE(emio_str == buf.data());\n\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), arg);\n  };\n  BENCHMARK(\"snprintf\") {\n    return snprintf(buf.data(), buf.size(), \"%e\", arg);\n  };\n}\n\nTEST_CASE(\"format double fixed\") {\n  static constexpr std::string_view format_str{\"{:f}\"};\n  static constexpr double arg = M_PI;\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, arg);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, arg);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, arg);\n    const std::string fmt_str = fmt::format(format_str, arg);\n    REQUIRE(emio_str == fmt_str);\n\n    REQUIRE(snprintf(buf.data(), buf.size(), \"%f\", arg) == static_cast<int>(emio_str.size()));\n    REQUIRE(emio_str == buf.data());\n\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), arg).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, arg);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), arg);\n  };\n  BENCHMARK(\"snprintf\") {\n    return snprintf(buf.data(), buf.size(), \"%f\", arg);\n  };\n}\n\nTEST_CASE(\"format many arguments\") {\n  static constexpr std::string_view format_str{\"{} {} {} {} {} {} {} {} {} {}\"};\n  // No floating-point because this shifts this benchmark result too much because it is much slower in emio than in fmt.\n#define ARGS                                                                                                \\\n  true, static_cast<int8_t>(1), static_cast<uint8_t>(2), static_cast<int16_t>(3), static_cast<uint16_t>(4), \\\n      static_cast<int32_t>(5), static_cast<uint32_t>(6), \"abc\", 'x', nullptr\n\n  constexpr size_t emio_formatted_size = emio::formatted_size(format_str, ARGS);\n  const size_t fmt_formatted_size = fmt::formatted_size(format_str, ARGS);\n  REQUIRE(emio_formatted_size == fmt_formatted_size);\n  std::array<char, 2 * emio_formatted_size> buf{};\n\n  BENCHMARK(\"base\") {\n    const std::string emio_str = emio::format(format_str, ARGS);\n    const std::string fmt_str = fmt::format(format_str, ARGS);\n    REQUIRE(emio_str == fmt_str);\n    return emio_str == fmt_str;\n  };\n  BENCHMARK(\"emio\") {\n    return emio::format_to(buf.data(), format_str, ARGS);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::format_to(buf.data(), emio::runtime(format_str), ARGS).value();\n  };\n  BENCHMARK(\"fmt\") {\n    return fmt::format_to(buf.data(), format_str, ARGS);\n  };\n  BENCHMARK(\"fmt runtime\") {\n    return fmt::format_to(buf.data(), fmt::runtime(format_str), ARGS);\n  };\n}\n"
  },
  {
    "path": "test/benchmark/bench_scan.cpp",
    "content": "// Unit under test.\n#include <fmt/format.h>\n\n#include <emio/scan.hpp>\n\n// Other includes.\n#include <catch2/benchmark/catch_benchmark.hpp>\n#include <catch2/catch_test_macros.hpp>\n#include <cinttypes>\n#include <cmath>\n\nTEST_CASE(\"scan nothing\") {\n  static constexpr std::string_view long_text(\n      \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore \"\n      \"magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo \"\n      \"consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla \"\n      \"pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est \"\n      \"laborum.\");\n\n  BENCHMARK(\"base\") {\n    REQUIRE(emio::scan(long_text, long_text));\n    REQUIRE(sscanf(long_text.data(), long_text.data()) == 0);\n  };\n  BENCHMARK(\"emio\") {\n    return emio::scan(long_text, long_text);\n  };\n  BENCHMARK(\"emio runtime\") {\n    return emio::scan(long_text, emio::runtime(long_text));\n  };\n  BENCHMARK(\"snprintf\") {\n    return sscanf(long_text.data(), long_text.data());\n  };\n}\n\nTEST_CASE(\"scan simple integer\") {\n  BENCHMARK(\"base\") {\n    return \"1\";\n  };\n  BENCHMARK(\"emio\") {\n    int i;\n    return emio::scan(\"1\", \"{}\", i);\n  };\n  BENCHMARK(\"emio runtime\") {\n    int i;\n    return emio::scan(\"1\", emio::runtime(\"{}\"), i);\n  };\n  BENCHMARK(\"snprintf\") {\n    int i;\n    return sscanf(\"1\", \"%d\", &i);\n  };\n}\n\nTEST_CASE(\"scan complex integer\") {\n  static constexpr std::string_view input(\"8978612134175239201\");\n\n  BENCHMARK(\"base\") {\n    int64_t i;\n    REQUIRE(emio::scan(input, \"{}\", i));\n    REQUIRE(i == 8978612134175239201);\n    i = 0;\n    REQUIRE(sscanf(input.data(), \"%\" PRIi64, &i) == 1);\n    REQUIRE(i == 8978612134175239201);\n  };\n  BENCHMARK(\"emio\") {\n    int64_t i;\n    return emio::scan(input, \"{}\", i);\n  };\n  BENCHMARK(\"emio runtime\") {\n    int64_t i;\n    return emio::scan(input, emio::runtime(\"{}\"), i);\n  };\n  BENCHMARK(\"snprintf\") {\n    int64_t i;\n    return sscanf(input.data(), \"%\" PRIi64, &i);\n  };\n}\n\nTEST_CASE(\"scan complex hex\") {\n  static constexpr std::string_view input(\"7C9A702A5186EC21\");\n\n  BENCHMARK(\"base\") {\n    uint64_t i;\n    REQUIRE(emio::scan(input, \"{:x}\", i));\n    REQUIRE(i == 0x7C9A702A5186EC21);\n    i = 0;\n    REQUIRE(sscanf(input.data(), \"%\" PRIx64, &i) == 1);\n    REQUIRE(i == 0x7C9A702A5186EC21);\n  };\n  BENCHMARK(\"emio\") {\n    uint64_t i;\n    return emio::scan(input, \"{:x}\", i);\n  };\n  BENCHMARK(\"emio runtime\") {\n    uint64_t i;\n    return emio::scan(input, emio::runtime(\"{:x}\"), i);\n  };\n  BENCHMARK(\"snprintf\") {\n    uint64_t i;\n    return sscanf(input.data(), \"%\" PRIx64, &i);\n  };\n}\n"
  },
  {
    "path": "test/compile_test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioTests LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/project-is-top-level.cmake)\ninclude(${CMAKE_SOURCE_DIR}/cmake/folders.cmake)\n\nif (PROJECT_IS_TOP_LEVEL)\n    find_package(emio REQUIRED)\n    enable_testing()\nendif ()\n\nadd_executable(emio_compile compile.cpp)\n\ntarget_link_libraries(emio_compile\n        emio::emio\n)\n\ntarget_compile_features(emio_compile PRIVATE cxx_std_20)\n\nadd_executable(emio_compile_freestanding compile.cpp)\n\ntarget_link_libraries(emio_compile_freestanding\n        emio::emio\n)\n\ntarget_compile_features(emio_compile_freestanding PRIVATE cxx_std_20)\ntarget_compile_options(emio_compile_freestanding PRIVATE -ffreestanding)\n\nadd_folders(CompileTest)\n"
  },
  {
    "path": "test/compile_test/compile.cpp",
    "content": "#include <emio/emio.hpp>\n\nconsteval auto created_with_span_buffer() {\n  std::array<char, 5> storage{};\n  emio::span_buffer buf{storage};\n  return storage;\n}\n\nconsteval auto created_with_static_buffer() {\n  std::array<char, 5> storage{};\n  emio::static_buffer<5> buf{};\n  std::span<char> area = buf.get_write_area_of(5).value();\n  std::fill(area.begin(), area.end(), 'a');\n  std::copy(buf.view().begin(), buf.view().end(), storage.begin());\n  return storage;\n}\n\nconsteval auto created_with_memory_buffer() {\n  std::array<char, 5> storage{};\n  emio::memory_buffer<0> buf{};\n  std::span<char> area = buf.get_write_area_of(5).value();\n  std::fill(area.begin(), area.end(), 'a');\n  std::copy(buf.view().begin(), buf.view().end(), storage.begin());\n  return storage;\n}\n\nconsteval auto created_with_iterator_buffer() {\n  std::array<char, 5> storage{};\n  emio::iterator_buffer buf{storage.data()};\n  std::span<char> area = buf.get_write_area_of(5).value();\n  std::fill(area.begin(), area.end(), 'a');\n  return storage;\n}\n\nextern \"C\" int main() {\n  const auto a = created_with_span_buffer();\n  const auto b = created_with_static_buffer();\n  const auto c = created_with_memory_buffer();\n  const auto d = created_with_iterator_buffer();\n\n  return a.size() + b.size() + c.size() + d.size();\n}\n"
  },
  {
    "path": "test/fuzzy/dragon4/.gitignore",
    "content": "bin\noutput\n"
  },
  {
    "path": "test/fuzzy/dragon4/Makefile",
    "content": "all: build-rust build-cpp fuzzy\n\nbuild-rust:\n\tcargo build --manifest-path rust_ref/Cargo.toml -Z unstable-options --out-dir bin/ --release\n\nbuild-cpp:\n\tafl-clang++ -O3 -s main.cpp -I../../../include -std=c++20 -l rust_ref -L bin/ -o bin/fuzzy\n\nfuzzy:\n\tLD_LIBRARY_PATH=bin AFL_BENCH_UNTIL_CRASH=1 afl-fuzz -i seeds/ -o output/ -D  -- ./bin/fuzzy\n"
  },
  {
    "path": "test/fuzzy/dragon4/main.cpp",
    "content": "#include <climits>\n\n#include \"emio/detail/format/decode.hpp\"\n#include \"emio/detail/format/dragon.hpp\"\n\nextern \"C\" {\n#include <unistd.h>\n}\n\n#ifndef __AFL_INIT\n#  define __AFL_INIT() \\\n    int _i_ = 0;       \\\n    read(0, &x, 8);\n#  define __AFL_FUZZ_INIT() double x{};\n#  define __AFL_FUZZ_TESTCASE_BUF &x\n#  define __AFL_LOOP(...) _i_++ == 0\n#  define __AFL_FUZZ_TESTCASE_LEN 8\n#endif\n\nextern \"C\" {\n\nstruct Buffer {\n  int16_t k;\n  size_t len;\n  uint8_t* data;\n};\n\nextern Buffer rust_shortest(double d);\nextern Buffer rust_fixed(double d, int16_t precision);\nextern Buffer rust_exponent(double d, int16_t precision);\nextern void rust_free(Buffer b);\n}\n\nnamespace {\n\nvoid print_shortest_header(double d) {\n  printf(\"shortest of %.17e\\r\\n\", d);\n}\n\nvoid print_fixed_header(double d, int16_t precision) {\n  printf(\"fixed of %.17e with a precision of %d\\r\\n\", d, precision);\n}\n\nvoid print_exact_header(double d, int16_t precision) {\n  printf(\"exact of %.17e with a length of %d\\r\\n\", d, precision);\n}\n\ntemplate <typename T>\nvoid print_result(std::string_view name, std::span<T> digits, int16_t k) {\n  printf(\"%.*s -> digits: %.*s k: %d \\r\\n\", static_cast<int>(name.size()), name.data(), static_cast<int>(digits.size()),\n         digits.data(), k);\n}\n\ntemplate <typename T>\nstd::span<T> remove_trailing_zeros(std::span<T> digits) {\n  auto it = std::find_if(digits.rbegin(), digits.rend(), [](char c) {\n    return c != '0';\n  });\n  return digits.subspan(0, it.base() - digits.begin());\n}\n\nvoid test_shortest(double d) {\n  Buffer b = rust_shortest(d);\n  int16_t rust_k = b.k;\n  std::span<const char> rust_digits{reinterpret_cast<const char*>(b.data), b.len};\n\n  auto full_decoded = emio::detail::format::decode(d);\n  if (full_decoded.category != emio::detail::format::category::finite) {\n    if (!rust_digits.empty() || rust_k != 0) {\n      print_shortest_header(d);\n      print_result(\"rust\", rust_digits, rust_k);\n      print_result(\"emio (not finite)\", std::span<char>{}, 0);\n      abort();\n    }\n  } else {\n    emio::memory_buffer buf;\n    auto [digits, k] = emio::detail::format::format_shortest(full_decoded.finite, buf);\n    if (!std::equal(rust_digits.begin(), rust_digits.end(), digits.begin(), digits.end()) || rust_k != k) {\n      print_shortest_header(d);\n      print_result(\"rust\", rust_digits, rust_k);\n      print_result(\"emio\", digits, k);\n      abort();\n    }\n  }\n  rust_free(b);\n}\n\nvoid test_fixed(double d, int16_t precision) {\n  precision = std::clamp<int16_t>(precision, -1000, 1000);\n\n  Buffer b = rust_fixed(d, precision);\n  int16_t rust_k = b.k;\n  std::span<const char> rust_digits{reinterpret_cast<const char*>(b.data), b.len};\n  rust_digits = remove_trailing_zeros(rust_digits);\n\n  auto full_decoded = emio::detail::format::decode(d);\n  if (full_decoded.category != emio::detail::format::category::finite) {\n    if (!rust_digits.empty() || rust_k != 0) {\n      print_fixed_header(d, precision);\n      print_result(\"rust\", rust_digits, rust_k);\n      print_result(\"emio (not finite)\", std::span<char>{}, 0);\n      abort();\n    }\n  } else {\n    emio::memory_buffer buf;\n    auto [digits, k] = emio::detail::format::format_exact(\n        full_decoded.finite, buf, emio::detail::format::format_exact_mode::decimal_point, precision);\n    digits = remove_trailing_zeros(digits);\n\n    if (!std::equal(rust_digits.begin(), rust_digits.end(), digits.begin(), digits.end()) || rust_k != k) {\n      print_fixed_header(d, precision);\n      print_result(\"rust\", rust_digits, rust_k);\n      print_result(\"emio\", digits, k);\n      abort();\n    }\n  }\n  rust_free(b);\n}\n\nvoid test_exact(double d, int16_t precision) {\n  precision = std::clamp<int16_t>(precision, 1, 1000);\n\n  Buffer b = rust_exponent(d, precision);\n  int16_t rust_k = b.k;\n  std::span<const char> rust_digits{reinterpret_cast<const char*>(b.data), b.len};\n  rust_digits = remove_trailing_zeros(rust_digits);\n\n  auto full_decoded = emio::detail::format::decode(d);\n  if (full_decoded.category != emio::detail::format::category::finite) {\n    if (!rust_digits.empty() || rust_k != 0) {\n      print_exact_header(d, precision);\n      print_result(\"rust\", rust_digits, rust_k);\n      print_result(\"emio (not finite)\", std::span<char>{}, 0);\n      abort();\n    }\n  } else {\n    emio::memory_buffer buf;\n    auto [digits, k] = emio::detail::format::format_exact(\n        full_decoded.finite, buf, emio::detail::format::format_exact_mode::significand_digits, precision);\n    digits = remove_trailing_zeros(digits);\n\n    if (!std::equal(rust_digits.begin(), rust_digits.end(), digits.begin(), digits.end()) || rust_k != k) {\n      print_exact_header(d, precision);\n      print_result(\"rust\", rust_digits, rust_k);\n      print_result(\"emio\", digits, k);\n      abort();\n    }\n  }\n  rust_free(b);\n}\n\n}  // namespace\n\n__AFL_FUZZ_INIT();\n\nint main() {\n  __AFL_INIT();\n  double* magic = (double*)__AFL_FUZZ_TESTCASE_BUF;\n\n  while (__AFL_LOOP(INT_MAX)) {\n    int len = __AFL_FUZZ_TESTCASE_LEN;\n    if (len < (sizeof(double))) continue;\n\n    double d = *magic;\n    test_shortest(d);\n\n    int16_t limit = 17;\n    if (len >= (sizeof(double) + sizeof(int16_t))) {\n      limit = *((int16_t*)(__AFL_FUZZ_TESTCASE_BUF + sizeof(double)));\n    }\n    test_fixed(d, limit);\n    test_exact(d, limit);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "test/fuzzy/dragon4/rust_ref/.gitignore",
    "content": "/target\n/Cargo.lock\n"
  },
  {
    "path": "test/fuzzy/dragon4/rust_ref/Cargo.toml",
    "content": "[package]\nname = \"rust_ref\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type   = [\"cdylib\"]\n\n#[unstable]\n#mtime-on-use = true\n#build-std = [\"core\", \"alloc\"]\n\n[dependencies]\n"
  },
  {
    "path": "test/fuzzy/dragon4/rust_ref/src/lib.rs",
    "content": "#![feature(flt2dec)]\n#![feature(maybe_uninit_uninit_array)]\n\nextern crate core;\n\nuse core::num::flt2dec::strategy::dragon::{format_exact, format_shortest};\nuse core::num::flt2dec::MAX_SIG_DIGITS;\n\nuse core::num::flt2dec::decode;\nuse core::num::flt2dec::decoder::FullDecoded;\nuse std::mem::MaybeUninit;\nuse std::ptr::null_mut;\n\n#[repr(C)]\nstruct Buffer {\n    k: i16,\n    len: usize,\n    data: *mut u8,\n}\n\n#[no_mangle]\nextern \"C\" fn rust_shortest(d: f64) -> Buffer {\n    let (_sign, full_decoded) = decode(d);\n    match full_decoded {\n        FullDecoded::Finite(decoded) => {\n            let mut buf: [MaybeUninit<u8>; MAX_SIG_DIGITS] = MaybeUninit::uninit_array();\n            let (buf, exp) = format_shortest(&decoded, &mut buf);\n\n            let mut vec = buf.to_vec();\n            let data = vec.as_mut_ptr();\n            let len = vec.len();\n            std::mem::forget(vec);\n            Buffer { k: exp, len, data }\n        }\n        _ => Buffer {\n            k: 0,\n            len: 0,\n            data: null_mut(),\n        },\n    }\n}\n\n#[no_mangle]\nextern \"C\" fn rust_fixed(d: f64, precision: i16) -> Buffer {\n    let (_sign, full_decoded) = decode(d);\n    match full_decoded {\n        FullDecoded::Finite(decoded) => {\n            let mut buf: [MaybeUninit<u8>; 1024] = MaybeUninit::uninit_array();\n            let (buf, exp) = format_exact(&decoded, &mut buf, -precision);\n\n            let mut vec = buf.to_vec();\n            let data = vec.as_mut_ptr();\n            let len = vec.len();\n            std::mem::forget(vec);\n            Buffer { k: exp, len, data }\n        }\n        _ => Buffer {\n            k: 0,\n            len: 0,\n            data: null_mut(),\n        },\n    }\n}\n\n#[no_mangle]\nextern \"C\" fn rust_exponent(d: f64, limit: i16) -> Buffer {\n    let (_sign, full_decoded) = decode(d);\n    match full_decoded {\n        FullDecoded::Finite(decoded) => {\n            let mut buf: [MaybeUninit<u8>; 1024] = MaybeUninit::uninit_array();\n            let mut buf = &mut buf[..limit as usize];\n            let (buf, exp) = format_exact(&decoded, &mut buf, i16::MIN);\n\n            let mut vec = buf.to_vec();\n            let data = vec.as_mut_ptr();\n            let len = vec.len();\n            std::mem::forget(vec);\n            Buffer { k: exp, len, data }\n        }\n        _ => Buffer {\n            k: 0,\n            len: 0,\n            data: null_mut(),\n        },\n    }\n}\n\n#[no_mangle]\nextern \"C\" fn rust_free(buf: Buffer) {\n    if buf.len == 0 {\n        return;\n    }\n    let s = unsafe { std::slice::from_raw_parts_mut(buf.data, buf.len as usize) };\n    let s = s.as_mut_ptr();\n    unsafe {\n        let _ = Box::from_raw(s);\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::str;\n\n    #[test]\n    fn it_works() {\n        let b = rust_fixed(0.1, 40);\n        let s = unsafe { std::slice::from_raw_parts_mut(b.data, b.len as usize) };\n        println!(\"{}; {}\", str::from_utf8(s).unwrap(), b.k);\n        rust_free(b);\n    }\n}\n"
  },
  {
    "path": "test/fuzzy/dragon4/seeds/test1",
    "content": "123456789101112131415\n"
  },
  {
    "path": "test/size_test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioSizeTests LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/project-is-top-level.cmake)\ninclude(${CMAKE_SOURCE_DIR}/cmake/folders.cmake)\n\nif (PROJECT_IS_TOP_LEVEL)\n    find_package(emio REQUIRED)\nendif ()\n\nmacro(create_size_test NAME)\n    string(REPLACE \"/\" \"_\" TARGET \"emio_size_test_${NAME}\")\n\n    add_executable(${TARGET}\n            stubs.cpp\n            ${NAME}\n            )\n\n    target_link_libraries(${TARGET} PRIVATE emio::emio fmt::fmt)\n    target_compile_features(${TARGET} PRIVATE cxx_std_20)\n\n    list(APPEND SIZE_COVERAGE_FILES ${PROJECT_BINARY_DIR}/${TARGET})\nendmacro()\n\ncreate_size_test(base.cpp) # Base file must be the first one.\ncreate_size_test(emio/doFormat_a.cpp)\ncreate_size_test(emio/format_int.cpp)\ncreate_size_test(emio/format_all.cpp)\ncreate_size_test(emio/format_all_and_extra.cpp)\ncreate_size_test(emio/format_and_scan_all.cpp)\ncreate_size_test(emio/format_and_scan_all_runtime.cpp)\ncreate_size_test(emio/format_and_write_int.cpp)\ncreate_size_test(emio/format_double.cpp)\ncreate_size_test(emio/format_runtime.cpp)\ncreate_size_test(emio/format_to.cpp)\ncreate_size_test(emio/format_to_n.cpp)\ncreate_size_test(emio/format_int_twice.cpp)\ncreate_size_test(emio/scan_all.cpp)\ncreate_size_test(emio/scan_int.cpp)\ncreate_size_test(emio/write_int.cpp)\n\ncreate_size_test(fmt/doFormat_a.cpp)\ncreate_size_test(fmt/fmt_dragon.cpp)\ncreate_size_test(fmt/fmt_grisu.cpp)\ncreate_size_test(fmt/fmt_grisu_and_dragon.cpp)\ncreate_size_test(fmt/format_int.cpp)\ncreate_size_test(fmt/format_all.cpp)\ncreate_size_test(fmt/format_all_and_extra.cpp)\ncreate_size_test(fmt/format_runtime.cpp)\ncreate_size_test(fmt/format_to.cpp)\ncreate_size_test(fmt/format_to_n.cpp)\ncreate_size_test(fmt/format_int_twice.cpp)\ncreate_size_test(fmt/vformat.cpp)\n\ncreate_size_test(std/locale.cpp)\ncreate_size_test(std/snprintf_and_sscanf.cpp)\ncreate_size_test(std/snprintf.cpp)\ncreate_size_test(std/sscanf.cpp)\ncreate_size_test(std/string.cpp)\ncreate_size_test(std/string_stream.cpp)\ncreate_size_test(std/to_chars.cpp)\ncreate_size_test(std/to_string_double.cpp)\ncreate_size_test(std/to_string_int.cpp)\n\noption(ENABLE_SIZE_COVERAGE \"Enable size-coverage support separate from CTest's\" OFF)\nif (ENABLE_SIZE_COVERAGE)\n    include(${CMAKE_SOURCE_DIR}/cmake/size-coverage.cmake)\nendif ()\n\nadd_folders(SizeTest)\n"
  },
  {
    "path": "test/size_test/base.cpp",
    "content": "int main() {}\n"
  },
  {
    "path": "test/size_test/emio/doFormat_a.cpp",
    "content": "#include <emio/format.hpp>\n\nint main() {\n  static_cast<void>(emio::format(\"{}\\n\", \"somefile.cpp\"));\n  static_cast<void>(emio::format(\"{}:{}\\n\", \"somefile.cpp\", 42));\n  static_cast<void>(emio::format(\"{}:{}:{}\\n\", \"somefile.cpp\", 42, \"asdf\"));\n  static_cast<void>(emio::format(\"{}:{}:{}:{}\\n\", \"somefile.cpp\", 42, 1, \"asdf\"));\n  static_cast<void>(emio::format(\"{}:{}:{}:{}:{}\\n\", \"somefile.cpp\", 42, 1, 2, \"asdf\"));\n}\n"
  },
  {
    "path": "test/size_test/emio/format_all.cpp",
    "content": "#include <emio/format.hpp>\n\nint main() {\n  static_cast<void>(emio::format(\"{} {} {} {} {} {} {} {} {} {} {}\", true, static_cast<int8_t>(1),\n                                 static_cast<uint8_t>(2), static_cast<int16_t>(3), static_cast<uint16_t>(4),\n                                 static_cast<int32_t>(5), static_cast<uint32_t>(6), \"abc\", 'x', nullptr, 4.2));\n}\n"
  },
  {
    "path": "test/size_test/emio/format_all_and_extra.cpp",
    "content": "#include <emio/emio.hpp>\n\nint main() {\n  static_cast<void>(emio::format(\"{} {} {} {} {} {} {} {} {} {} {} {} {} {}\", true, static_cast<int8_t>(1),\n                                 static_cast<uint8_t>(2), static_cast<int16_t>(3), static_cast<uint16_t>(4),\n                                 static_cast<int32_t>(5), static_cast<uint32_t>(6), \"abc\", 'x', nullptr, 4.2,\n                                 std::tuple<int, double>{7, 2.6}, std::array<int, 2>{{8, 9}},\n                                 emio::format_spec{.width = 5}.with(5)));\n}\n"
  },
  {
    "path": "test/size_test/emio/format_and_scan_all.cpp",
    "content": "#include <emio/emio.hpp>\n\nint main(int /*c*/, char* args[]) {\n  static_cast<void>(emio::format(\"{} {} {} {} {} {} {} {} {} {} {}\", true, static_cast<int8_t>(1),\n                                 static_cast<uint8_t>(2), static_cast<int16_t>(3), static_cast<uint16_t>(4),\n                                 static_cast<int32_t>(5), static_cast<uint32_t>(6), \"abc\", 'x', nullptr, 4.2));\n\n  char ch{};\n  int8_t i8{};\n  uint8_t u8{};\n  int16_t i16{};\n  uint16_t u16{};\n  int32_t i32{};\n  uint32_t u32{};\n  int64_t i64{};\n  uint64_t u64{};\n  std::string_view str{};\n  static_cast<void>(emio::scan(args[0], \"{}{}{}{}{}{}{}{}{}{}\", ch, i8, u8, i16, u16, i32, u32, i64, u64, str));\n}\n"
  },
  {
    "path": "test/size_test/emio/format_and_scan_all_runtime.cpp",
    "content": "#include <emio/emio.hpp>\n\nint main(int /*c*/, char* args[]) {\n  static_cast<void>(emio::format(emio::runtime(\"{} {} {} {} {} {} {} {} {} {} {}\"), true, static_cast<int8_t>(1),\n                                 static_cast<uint8_t>(2), static_cast<int16_t>(3), static_cast<uint16_t>(4),\n                                 static_cast<int32_t>(5), static_cast<uint32_t>(6), \"abc\", 'x', nullptr, 4.2));\n\n  char ch{};\n  int8_t i8{};\n  uint8_t u8{};\n  int16_t i16{};\n  uint16_t u16{};\n  int32_t i32{};\n  uint32_t u32{};\n  int64_t i64{};\n  uint64_t u64{};\n  std::string_view str{};\n  static_cast<void>(\n      emio::scan(args[0], emio::runtime(\"{}{}{}{}{}{}{}{}{}{}\"), ch, i8, u8, i16, u16, i32, u32, i64, u64, str));\n}\n"
  },
  {
    "path": "test/size_test/emio/format_and_write_int.cpp",
    "content": "#include <emio/format.hpp>\n\nint main() {\n  emio::memory_buffer buf;\n  emio::writer wrt{buf};\n  wrt.write_int(1).value();\n  static_cast<void>(buf.str());\n  static_cast<void>(emio::format(\"{}\", 1));\n}\n"
  },
  {
    "path": "test/size_test/emio/format_double.cpp",
    "content": "#include <emio/format.hpp>\n\nint main() {\n  static_cast<void>(emio::format(\"{}\", 1.0));\n}\n"
  },
  {
    "path": "test/size_test/emio/format_int.cpp",
    "content": "#include <emio/format.hpp>\n\nint main() {\n  static_cast<void>(emio::format(\"{}\", 1));\n}\n"
  },
  {
    "path": "test/size_test/emio/format_int_twice.cpp",
    "content": "#include <emio/format.hpp>\n\nint main() {\n  static_cast<void>(emio::format(\"{}\", 1));\n  static_cast<void>(emio::format(\"{}\", 2));\n}\n"
  },
  {
    "path": "test/size_test/emio/format_runtime.cpp",
    "content": "#include <emio/format.hpp>\n\nint main() {\n  emio::format(emio::runtime(\"{}\"), 1).value();\n}\n"
  },
  {
    "path": "test/size_test/emio/format_to.cpp",
    "content": "#include <array>\n#include <emio/format.hpp>\n\nint main() {\n  std::array<char, 1> arr;\n  emio::span_buffer buf{arr};\n  emio::format_to(buf, \"{}\", 1).value();\n}\n"
  },
  {
    "path": "test/size_test/emio/format_to_n.cpp",
    "content": "#include <array>\n#include <emio/format.hpp>\n\nint main() {\n  std::array<char, 1> arr;\n  emio::format_to_n(arr.begin(), 1, \"{}\", 1).value();\n}\n"
  },
  {
    "path": "test/size_test/emio/scan_all.cpp",
    "content": "#include <emio/scan.hpp>\n\nint main(int /*c*/, char* args[]) {\n  char ch{};\n  int8_t i8{};\n  uint8_t u8{};\n  int16_t i16{};\n  uint16_t u16{};\n  int32_t i32{};\n  uint32_t u32{};\n  int64_t i64{};\n  uint64_t u64{};\n  std::string_view sv{};\n  static_cast<void>(emio::scan(args[0], \"{}{}{}{}{}{}{}{}{}{}\", ch, i8, u8, i16, u16, i32, u32, i64, u64, sv));\n}\n"
  },
  {
    "path": "test/size_test/emio/scan_int.cpp",
    "content": "#include <emio/scan.hpp>\n\nint main(int /*c*/, char* args[]) {\n  int i{};\n  static_cast<void>(emio::scan(args[0], \"{}\", i));\n}\n"
  },
  {
    "path": "test/size_test/emio/vformat.cpp",
    "content": "#include <emio/format.hpp>\n\nint main() {\n  emio::vformat(emio::make_format_args(\"{}\", 1)).value();\n}\n"
  },
  {
    "path": "test/size_test/emio/write_int.cpp",
    "content": "#include <emio/format.hpp>\n\nint main() {\n  emio::memory_buffer buf;\n  emio::writer wrt{buf};\n  wrt.write_int(1).value();\n  static_cast<void>(buf.str());\n}\n"
  },
  {
    "path": "test/size_test/fmt/doFormat_a.cpp",
    "content": "#include <fmt/core.h>\n\n#include <array>\n\nint main() {\n  static_cast<void>(fmt::format(\"{}\\n\", \"somefile.cpp\"));\n  static_cast<void>(fmt::format(\"{}:{}\\n\", \"somefile.cpp\", 42));\n  static_cast<void>(fmt::format(\"{}:{}:{}\\n\", \"somefile.cpp\", 42, \"asdf\"));\n  static_cast<void>(fmt::format(\"{}:{}:{}:{}\\n\", \"somefile.cpp\", 42, 1, \"asdf\"));\n  static_cast<void>(fmt::format(\"{}:{}:{}:{}:{}\\n\", \"somefile.cpp\", 42, 1, 2, \"asdf\"));\n}\n"
  },
  {
    "path": "test/size_test/fmt/fmt_dragon.cpp",
    "content": "#include \"fmt/format.h\"\n\nint main(int c, char*[] /*unused*/) {\n  int exp{};\n  fmt::basic_memory_buffer<char> b;\n  fmt::detail::format_dragon(static_cast<double>(c) / 3.8f, fmt::detail::dragon::fixed, 6, b, exp);\n}\n"
  },
  {
    "path": "test/size_test/fmt/fmt_grisu.cpp",
    "content": "#include \"fmt/format.h\"\n\nint main(int c, char*[] /*unused*/) {\n  int exp{};\n  fmt::basic_memory_buffer<char> b;\n  fmt::detail::gen_digits_handler gdh{b.data(), 0, 1, 2, {}};\n  fmt::detail::grisu_gen_digits(static_cast<double>(c) / 3.8f, 1, exp, gdh);\n}\n"
  },
  {
    "path": "test/size_test/fmt/fmt_grisu_and_dragon.cpp",
    "content": "#include \"fmt/format.h\"\n\nint main(int c, char*[] /*unused*/) {\n  int exp{};\n  fmt::basic_memory_buffer<char> b;\n  fmt::detail::gen_digits_handler gdh{b.data(), 0, 1, 2, {}};\n  fmt::detail::grisu_gen_digits(static_cast<double>(c) / 3.8f, 1, exp, gdh);\n  fmt::detail::format_dragon(static_cast<double>(c) / 3.8f, fmt::detail::dragon::fixed, 6, b, exp);\n}\n"
  },
  {
    "path": "test/size_test/fmt/format_all.cpp",
    "content": "#include <fmt/core.h>\n\nint main() {\n  static_cast<void>(fmt::format(\"{} {} {} {} {} {} {} {} {} {} {}\", true, static_cast<int8_t>(1),\n                                static_cast<uint8_t>(2), static_cast<int16_t>(3), static_cast<uint16_t>(4),\n                                static_cast<int32_t>(5), static_cast<uint32_t>(6), \"abc\", 'x', nullptr, 4.2));\n}\n"
  },
  {
    "path": "test/size_test/fmt/format_all_and_extra.cpp",
    "content": "#include <fmt/core.h>\n#include <fmt/ranges.h>\n\n#include <array>\n\nint main() {\n  static_cast<void>(fmt::format(\"{} {} {} {} {} {} {} {} {} {} {} {} {}\", true, static_cast<int8_t>(1),\n                                static_cast<uint8_t>(2), static_cast<int16_t>(3), static_cast<uint16_t>(4),\n                                static_cast<int32_t>(5), static_cast<uint32_t>(6), \"abc\", 'x', nullptr, 4.2,\n                                std::tuple<int, double>{7, 2.6}, std::array<int, 2>{{8, 9}}));\n}\n"
  },
  {
    "path": "test/size_test/fmt/format_int.cpp",
    "content": "#include <fmt/core.h>\n\nint main() {\n  static_cast<void>(fmt::format(\"{}\", 1));\n}\n"
  },
  {
    "path": "test/size_test/fmt/format_int_twice.cpp",
    "content": "#include <fmt/core.h>\n\nint main() {\n  static_cast<void>(fmt::format(\"{}\", 1));\n  static_cast<void>(fmt::format(\"{}\", 2));\n}\n"
  },
  {
    "path": "test/size_test/fmt/format_runtime.cpp",
    "content": "#include <fmt/core.h>\n\nint main() {\n  static_cast<void>(fmt::format(fmt::runtime(\"{}\"), 1));\n}\n"
  },
  {
    "path": "test/size_test/fmt/format_to.cpp",
    "content": "#include <fmt/core.h>\n\n#include <array>\n\nint main() {\n  std::array<char, 1> arr;\n  static_cast<void>(fmt::format_to(arr.begin(), \"{}\", 1));\n}\n"
  },
  {
    "path": "test/size_test/fmt/format_to_n.cpp",
    "content": "#include <fmt/core.h>\n\n#include <array>\n\nint main() {\n  std::array<char, 1> arr;\n  static_cast<void>(fmt::format_to_n(arr.begin(), 1, \"{}\", 1));\n}\n"
  },
  {
    "path": "test/size_test/fmt/vformat.cpp",
    "content": "#include <fmt/core.h>\n\nint main() {\n  static_cast<void>(fmt::vformat(\"{}\", fmt::make_format_args(1)));\n}\n"
  },
  {
    "path": "test/size_test/std/locale.cpp",
    "content": "#include <locale>\n\nint main() {\n  std::locale l;\n  static_cast<void>(l.name());\n}\n"
  },
  {
    "path": "test/size_test/std/snprintf.cpp",
    "content": "#include <array>\n#include <cinttypes>\n#include <cstdio>\n\nint main(int c, char* args[]) {\n  std::array<char, 42> s{};\n  void* null = nullptr;\n  int n = snprintf(s.data(), s.size(), \"%d %\" PRIu64 \" %x %.*s, %p %f %e %g %a\", -42, uint64_t{1}, 48, 0, args[0], null,\n                   static_cast<double>(c) / 3.14, static_cast<double>(c) / 3.14, static_cast<double>(c) / 3.14,\n                   static_cast<double>(c) / 3.14);\n  return s[1] + n;\n}\n"
  },
  {
    "path": "test/size_test/std/snprintf_and_sscanf.cpp",
    "content": "#include <array>\n#include <cinttypes>\n#include <cstdio>\n\nint main(int c, char* args[]) {\n  std::array<char, 42> s{};\n  void* null = nullptr;\n  int n = snprintf(s.data(), s.size(), \"%d %\" PRIu64 \" %x %.*s, %p %f %e %g %a\", -42, uint64_t{1}, 48, 0, args[0], null,\n                   static_cast<double>(c) / 3.14, static_cast<double>(c) / 3.14, static_cast<double>(c) / 3.14,\n                   static_cast<double>(c) / 3.14);\n\n  char ch{};\n  int8_t i8{};\n  uint8_t u8{};\n  int16_t i16{};\n  uint16_t u16{};\n  int32_t i32{};\n  uint32_t u32{};\n  int64_t i64{};\n  uint64_t u64{};\n  n += sscanf(args[0], \"%c%\" SCNd8 \"%\" SCNu8 \"%\" SCNd16 \"%\" SCNu16 \"%\" SCNd32 \"%\" SCNu32 \"%\" SCNd64 \"%\" SCNu64, &ch,\n              &i8, &u8, &i16, &u16, &i32, &u32, &i64, &u64);\n  return n;\n}\n"
  },
  {
    "path": "test/size_test/std/sscanf.cpp",
    "content": "#include <array>\n#include <cinttypes>\n#include <cstdio>\n\nint main(int /*c*/, char* args[]) {\n  char ch{};\n  int8_t i8{};\n  uint8_t u8{};\n  int16_t i16{};\n  uint16_t u16{};\n  int32_t i32{};\n  uint32_t u32{};\n  int64_t i64{};\n  uint64_t u64{};\n  int n = sscanf(args[0], \"%c%\" SCNd8 \"%\" SCNu8 \"%\" SCNd16 \"%\" SCNu16 \"%\" SCNd32 \"%\" SCNu32 \"%\" SCNd64 \"%\" SCNu64, &ch,\n                 &i8, &u8, &i16, &u16, &i32, &u32, &i64, &u64);\n  return n;\n}\n"
  },
  {
    "path": "test/size_test/std/string.cpp",
    "content": "#include <string>\n\nint main() {\n  std::string s{\"abcdefghijklmnopqrstuvwxyz\"};\n  static_cast<void>(s.c_str());\n}\n"
  },
  {
    "path": "test/size_test/std/string_stream.cpp",
    "content": "#include <sstream>\n\nint main() {\n  std::stringstream ss;\n  ss << 1;\n  static_cast<void>(ss.str());\n}\n"
  },
  {
    "path": "test/size_test/std/to_chars.cpp",
    "content": "#include <charconv>\n#include <string>\n\nint main() {\n  std::string s;\n  s.resize(42);\n  std::to_chars(s.data(), s.data() + 10, 1.42);\n  return s[1];\n}\n"
  },
  {
    "path": "test/size_test/std/to_string_double.cpp",
    "content": "#include <string>\n\nint main() {\n  return static_cast<int>(std::to_string(12345678.0).size());\n}\n"
  },
  {
    "path": "test/size_test/std/to_string_int.cpp",
    "content": "#include <string>\n\nint main() {\n  return static_cast<int>(std::to_string(12345678).size());\n}\n"
  },
  {
    "path": "test/size_test/stubs.cpp",
    "content": "extern \"C\" {\n\nvoid _exit(int /*unused*/) {\n  while (1) {\n  }\n}\n\nint _getpid() {\n  return 1;\n}\n\nint _kill(int /*unused*/, int /*unused*/) {\n  return -1;\n}\n\nvoid* _sbrk(int /*unused*/) {\n  return 0;\n}\n\nint _isatty(int /*unused*/) {\n  return 1;\n}\n\nint _lseek(int /*unused*/, int /*unused*/, int /*unused*/) {\n  return 0;\n}\n\nstruct stat;\n\nint _fstat(int /*unused*/, struct stat* /*st*/) {\n  return 0;\n}\n\nint _read(int /*unused*/, void* /*unused*/) {\n  return 0;\n}\n\nint _write(int /*unused*/, void* /*unused*/) {\n  return 0;\n}\n\nint _close(int /*unused*/) {\n  return -1;\n}\n}\n\n#include <exception>\n\nnamespace __cxxabiv1 {\n\nstd::terminate_handler __terminate_handler = nullptr;\n\n}  // namespace __cxxabiv1\n"
  },
  {
    "path": "test/static_analysis/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioStaticAnalysis LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/project-is-top-level.cmake)\ninclude(${CMAKE_SOURCE_DIR}/cmake/folders.cmake)\n\nif (PROJECT_IS_TOP_LEVEL)\n    find_package(emio REQUIRED)\nendif ()\n\nadd_executable(emio_static_analysis test_main.cpp)\ntarget_link_libraries(emio_static_analysis emio::emio)\ntarget_compile_features(emio_static_analysis PRIVATE cxx_std_20)\n\nadd_folders(StaticAnalysis)\n"
  },
  {
    "path": "test/static_analysis/test_main.cpp",
    "content": "// Include all.\n#include <emio/emio.hpp>\n\nconsteval emio::result<void> compile_time_test() {\n  emio::static_buffer<128> buf;\n  EMIO_TRYV(emio::format_to(buf, \"abc\"));\n  return emio::format_to(buf, \"abc {}\", 13);\n}\n\nemio::result<void> test() {\n  EMIO_TRYV(compile_time_test());\n  if (emio::format(\"abc\").size() != 3) {\n    return emio::err::invalid_data;\n  }\n  if (emio::format(\"{}\", \"abc\").size() != 3) {\n    return emio::err::invalid_data;\n  }\n  return emio::format(emio::runtime(\"abc {}\"), 13);\n}\n\nint main() {\n  if (test().has_error()) {\n    return 1;\n  }\n  return 0;\n}\n"
  },
  {
    "path": "test/unit_test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioTests LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/project-is-top-level.cmake)\ninclude(${CMAKE_SOURCE_DIR}/cmake/folders.cmake)\n\nif (PROJECT_IS_TOP_LEVEL)\n    find_package(emio REQUIRED)\n    enable_testing()\nendif ()\n\nadd_executable(emio_test\n        detail/test_bignum.cpp\n        detail/test_bitset.cpp\n        detail/test_conversion.cpp\n        detail/test_ct_vector.cpp\n        detail/test_decode.cpp\n        detail/test_dragon.cpp\n        detail/test_utf.cpp\n        test_buffer.cpp\n        test_dynamic_format_spec.cpp\n        test_format.cpp\n        test_format_api.cpp\n        test_format_as.cpp\n        test_format_emio_vs_fmt.cpp\n        test_format_string.cpp\n        test_format_to_api.cpp\n        test_format_could_fail_api.cpp\n        test_format_to_n_api.cpp\n        test_formatted_size.cpp\n        test_formatter.cpp\n        test_iterator.cpp\n        test_print.cpp\n        test_ranges.cpp\n        test_reader.cpp\n        test_result.cpp\n        test_scan.cpp\n        test_std.cpp\n        test_writer.cpp\n)\n\ntarget_link_libraries(emio_test\n        Catch2::Catch2WithMain\n        emio::emio\n        fmt::fmt\n)\n\ntarget_compile_features(emio_test PRIVATE cxx_std_20)\n\ntarget_compile_definitions(emio_test PRIVATE\n        EMIO_ENABLE_DEV_ASSERT\n)\n\nadd_test(NAME emio_test COMMAND emio_test)\n\nadd_folders(UnitTest)\n"
  },
  {
    "path": "test/unit_test/detail/test_bignum.cpp",
    "content": "// Unit under test.\n#include <emio/detail/bignum.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nTEST_CASE(\"bignum\") {\n  using emio::detail::bignum;\n  using emio::detail::borrowing_sub;\n  using emio::detail::borrowing_sub_result_t;\n  using emio::detail::carrying_add;\n  using emio::detail::carrying_add_result_t;\n  using emio::detail::carrying_mul;\n  using emio::detail::carrying_mul_result_t;\n\n  const uint32_t max = std::numeric_limits<uint32_t>::max();\n\n  SECTION(\"carrying_add\") {\n    using res_t = carrying_add_result_t;\n    CHECK(carrying_add(0, 0, false) == res_t{0, false});\n    CHECK(carrying_add(0, 0, true) == res_t{1, false});\n    CHECK(carrying_add(1, 0, false) == res_t{1, false});\n    CHECK(carrying_add(1, 1, false) == res_t{2, false});\n    CHECK(carrying_add(1, 1, true) == res_t{3, false});\n    CHECK(carrying_add(max, 1, false) == res_t{0, true});\n    CHECK(carrying_add(max, 1, true) == res_t{1, true});\n    CHECK(carrying_add(1, max, false) == res_t{0, true});\n    CHECK(carrying_add(1, max, true) == res_t{1, true});\n    CHECK(carrying_add(max, max, false) == res_t{max - 1, true});\n    CHECK(carrying_add(max, max, true) == res_t{max, true});\n  }\n\n  SECTION(\"borrowing_sub\") {\n    using res_t = borrowing_sub_result_t;\n    CHECK(borrowing_sub(0, 0, false) == res_t{0, false});\n    CHECK(borrowing_sub(0, 0, true) == res_t{max, true});\n    CHECK(borrowing_sub(1, 0, false) == res_t{1, false});\n    CHECK(borrowing_sub(1, 1, false) == res_t{0, false});\n    CHECK(borrowing_sub(1, 1, true) == res_t{max, true});\n    CHECK(borrowing_sub(max, 1, false) == res_t{max - 1, false});\n    CHECK(borrowing_sub(max, 1, true) == res_t{max - 2, false});\n    CHECK(borrowing_sub(1, max, false) == res_t{2, true});\n    CHECK(borrowing_sub(1, max, true) == res_t{1, true});\n    CHECK(borrowing_sub(max, max, false) == res_t{0, false});\n    CHECK(borrowing_sub(max, max, true) == res_t{max, true});\n  }\n\n  SECTION(\"carrying_mul\") {\n    using res_t = carrying_mul_result_t;\n    CHECK(carrying_mul(0, 0, 0) == res_t{0, 0});\n    CHECK(carrying_mul(0, 0, 4) == res_t{4, 0});\n    CHECK(carrying_mul(2, 0, 0) == res_t{0, 0});\n    CHECK(carrying_mul(2, 3, 0) == res_t{6, 0});\n    CHECK(carrying_mul(2, 3, 4) == res_t{10, 0});\n\n    CHECK(carrying_mul(max, max, 0) == res_t{1, max - 1});\n    CHECK(carrying_mul(max, max - 1, 0) == res_t{2, max - 2});\n    CHECK(carrying_mul(max, max, 9) == res_t{10, max - 1});\n    CHECK(carrying_mul(max, max, max) == res_t{0, max});\n  }\n\n  SECTION(\"from small\") {\n    const uint32_t v = 15;\n    const bignum b{v};\n    CHECK(b == bignum::from(1, {15, 0}));\n  }\n\n  SECTION(\"from_u64\") {\n    SECTION(\"<= 32\") {\n      const uint64_t v = 15;\n      const bignum b{v};\n      CHECK(b == bignum::from(1, {15, 0}));\n    }\n    SECTION(\">= 32\") {\n      const uint64_t v = 592359492949;\n      const bignum b{v};\n      CHECK(b == bignum::from(2, {3948973397, 137}));\n    }\n  }\n\n  SECTION(\"get_bit\") {\n    const bignum b{0x8004000044000008};\n    const std::array bits_set = {63, 50, 30, 26, 3};\n    for (size_t i = 0; i < 64; i++) {\n      if (std::find(bits_set.begin(), bits_set.end(), i) != bits_set.end()) {\n        CHECK(b.get_bit(i) == 1);\n      } else {\n        CHECK(b.get_bit(i) == 0);\n      }\n    }\n  }\n\n  SECTION(\"is_zero\") {\n    CHECK(bignum{0U}.is_zero());\n    CHECK(!bignum{129U}.is_zero());\n    CHECK(!bignum{592359492949U}.is_zero());\n  }\n\n  SECTION(\"add_small\") {\n    bignum b{max - 1};\n    SECTION(\"small steps\") {\n      b.add_small(1);\n      CHECK(b == bignum::from(1, {max, 0}));\n\n      b.add_small(3);\n      CHECK(b == bignum::from(2, {2, 1}));\n    }\n    SECTION(\"one step\") {\n      b.add_small(4);\n      CHECK(b == bignum::from(2, {2, 1}));\n    }\n  }\n\n  SECTION(\"add\") {\n    bignum a{0x1598468598486213U};\n    bignum b{0x400200410040010U};\n    const bignum c{0x954410040010U};\n    const bignum d{0x8918918U};\n\n    SECTION(\"a + b\") {\n      CHECK(a.add(b) == bignum{0x19986689a84c6223U});\n    }\n    SECTION(\"b + c\") {\n      CHECK(b.add(c) == bignum{0x400b54820080020U});\n    }\n    SECTION(\"a + d\") {\n      CHECK(a.add(d) == bignum{0x15984685a0d9eb2bU});\n    }\n  }\n\n  SECTION(\"sub_small\") {\n    bignum b = bignum::from(2, {2, 1});\n    SECTION(\"small steps\") {\n      b.sub_small(1);\n      CHECK(b == bignum::from(2, {1, 1}));\n\n      b.sub_small(1);\n      CHECK(b == bignum::from(2, {0, 1}));\n\n      b.sub_small(3);\n      CHECK(b == bignum::from(1, {max - 2, 0}));\n    }\n    SECTION(\"one step\") {\n      b.sub_small(5);\n      CHECK(b == bignum::from(1, {max - 2, 0}));\n\n      b.sub_small(max - 2);\n      CHECK(b == bignum::from(1, {0, 0}));\n    }\n  }\n\n  SECTION(\"sub\") {\n    bignum a = bignum::from(3, {4, 1, 2});\n    const bignum b = bignum::from(3, {2, 1, 2});\n    const bignum c = bignum::from(2, {4, 1});\n    const bignum d = bignum::from(1, {2});\n    SECTION(\"a\") {\n      SECTION(\"- a\") {\n        CHECK(a.sub(a) == bignum::from(0, {}));\n      }\n      SECTION(\"- b\") {\n        CHECK(a.sub(b) == bignum::from(1, {2}));\n        SECTION(\"- d\") {\n          CHECK(a.sub(d) == bignum::from(0, {0}));\n        }\n      }\n      SECTION(\"- c\") {\n        CHECK(a.sub(c) == bignum::from(3, {0, 0, 2}));\n        SECTION(\"- c\") {\n          CHECK(a.sub(c) == bignum::from(3, {max - 3, max - 1, 1}));\n          SECTION(\"- d\") {\n            CHECK(a.sub(d) == bignum::from(3, {max - 5, max - 1, 1}));\n          }\n        }\n        SECTION(\"- d\") {\n          CHECK(a.sub(d) == bignum::from(3, {max - 1, max, 1}));\n        }\n      }\n    }\n  }\n\n  SECTION(\"mul_small\") {\n    bignum a = bignum::from(3, {4, 1, 2});\n    CHECK(a.mul_small(2) == bignum::from(3, {8, 2, 4}));\n    CHECK(a.mul_small(3) == bignum::from(3, {24, 6, 12}));\n    CHECK(a.mul_small(360000000) == bignum::from(4, {50065408, 2160000002, 25032704, 1}));\n    CHECK(a.mul_small(0) == bignum::from(4, {0, 0, 0, 0}));\n  }\n\n  SECTION(\"mul_small_add\") {\n    bignum a = bignum::from(3, {4, 1, 5});\n    CHECK(a.muladd_small(1860000000, 15) == bignum::from(4, {3145032719, 1860000001, 710065408, 2}));\n  }\n\n  SECTION(\"mul\") {\n    SECTION(\"test 1\") {\n      const bignum a = bignum::from(2, {117327783, 607});\n      const bignum b = bignum::from(2, {152131585, 547});\n      CHECK(a.mul(b) == bignum::from(3, {280407975, 1907502595, 332065}));\n    }\n    SECTION(\"test 2\") {\n      const bignum a = bignum::from(3, {959858, 1547, 499669885});\n      const bignum b = bignum::from(6, {18959849, 65907, 594953, 9985999, 5499, 598879596});\n      CHECK(a.mul(b) == bignum::from(9, {986308290, 2397938630, 2743294157, 1925473916, 3187470779, 4197912536,\n                                         1955690142, 2089129235, 69672730}));\n    }\n  }\n\n  SECTION(\"mul_pow5\") {\n    SECTION(\"test 1\") {\n      bignum a = bignum::from(1, {2});\n      CHECK(a.mul_pow5(5) == bignum::from(1, {6250}));\n    }\n    SECTION(\"test 2\") {\n      bignum a = bignum::from(2, {1898, 125});\n      CHECK(a.mul_pow5(13) == bignum::from(3, {1907158706, 2264035804, 35}));\n    }\n    SECTION(\"test 3\") {\n      bignum a = bignum::from(2, {79581, 498859848});\n      CHECK(a.mul_pow5(57) ==\n            bignum::from(7, {2630842737, 672537777, 2761821040, 1492594830, 2410358682, 1582604142, 2}));\n    }\n  }\n\n  SECTION(\"div_rem_small\") {\n    SECTION(\"test 1\") {\n      bignum a{0x1598468598486213U};\n      CHECK(a.div_rem_small(0x44F2E41D) == 0xA83F4B9);\n      CHECK(a == bignum::from(2, {0x502DEFA2, 0}));\n    }\n    SECTION(\"test 2\") {\n      bignum a{0x400200410040010U};\n      CHECK(a.div_rem_small(0x56489623) == 0x450C3720);\n      CHECK(a == bignum::from(2, {0xBDE8A50, 0}));\n    }\n    SECTION(\"test 3\") {\n      bignum a{0x1895948989562198U};\n      CHECK(a.div_rem_small(0x7) == 2);\n      CHECK(a == bignum::from(2, {0x3830E03A, 0x3831538}));\n    }\n    SECTION(\"test 4\") {\n      bignum a{0x1895948989562198U};\n      CHECK(a.div_rem_small(0x1) == 0);\n      CHECK(a == bignum{0x1895948989562198U});\n    }\n    SECTION(\"test 5\") {\n      bignum a{0x240010U};\n      CHECK(a.div_rem_small(0x1548) == 0x148);\n      CHECK(a == bignum::from(1, {0x1B1}));\n    }\n    SECTION(\"test 6\") {\n      bignum a{0x240010U};\n      CHECK(a.div_rem_small(0x240020) == 0x240010);\n      CHECK(a == bignum::from(1, {0}));\n    }\n  }\n\n  SECTION(\"mul_pow2\") {\n    SECTION(\"test 1\") {\n      bignum a = bignum::from(3, {0x1, 0x3, 0x2});\n      CHECK(a.mul_pow2(1) == bignum::from(3, {0x2, 0x6, 0x4}));\n      CHECK(a.mul_pow2(9) == bignum::from(3, {0x400, 0xC00, 0x800}));\n      CHECK(a.mul_pow2(32) == bignum::from(4, {0x0, 0x400, 0xC00, 0x800}));\n      CHECK(a.mul_pow2(67) == bignum::from(6, {0, 0, 0, 0x2000, 0x6000, 0x4000}));\n      CHECK(a.mul_pow2(19) == bignum::from(7, {0, 0, 0, 0, 0x1, 0x3, 0x2}));\n    }\n  }\n\n  SECTION(\"three-way comparison\") {\n    const bignum a = bignum::from(3, {5, 1, 3});\n\n    SECTION(\"with a equal one\") {\n      const bignum b = bignum::from(3, {5, 1, 3});\n\n      CHECK(a >= b);\n      CHECK(a <= b);\n      CHECK(a == b);\n      CHECK_FALSE(a > b);\n      CHECK_FALSE(a < b);\n      CHECK_FALSE(a != b);\n\n      CHECK(b >= a);\n      CHECK(b <= a);\n      CHECK(b == a);\n      CHECK_FALSE(b > a);\n      CHECK_FALSE(b < a);\n      CHECK_FALSE(b != a);\n    }\n\n    SECTION(\"with a smaller one (smaller dimension)\") {\n      const bignum b = bignum::from(2, {1, 3});\n\n      CHECK(a >= b);\n      CHECK_FALSE(a <= b);\n      CHECK_FALSE(a == b);\n      CHECK(a > b);\n      CHECK_FALSE(a < b);\n      CHECK(a != b);\n\n      CHECK_FALSE(b >= a);\n      CHECK(b <= a);\n      CHECK_FALSE(b == a);\n      CHECK_FALSE(b > a);\n      CHECK(b < a);\n      CHECK(b != a);\n    }\n\n    SECTION(\"with a smaller one (same dimension)\") {\n      const bignum b = bignum::from(2, {4, 1, 3});\n\n      CHECK(a >= b);\n      CHECK_FALSE(a <= b);\n      CHECK_FALSE(a == b);\n      CHECK(a > b);\n      CHECK_FALSE(a < b);\n      CHECK(a != b);\n\n      CHECK_FALSE(b >= a);\n      CHECK(b <= a);\n      CHECK_FALSE(b == a);\n      CHECK_FALSE(b > a);\n      CHECK(b < a);\n      CHECK(b != a);\n    }\n  }\n}\n"
  },
  {
    "path": "test/unit_test/detail/test_bitset.cpp",
    "content": "// Unit under test.\n#include <emio/detail/bitset.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nTEST_CASE(\"bitset\") {\n  // Test strategy:\n  // * Create bitsets of different length and test all methods.\n  // Expected: Methods behave as expected.\n\n  using emio::detail::bitset;\n\n  SECTION(\"0\") {\n    bitset<0> bitset;\n    CHECK(bitset.size() == 0);\n    CHECK(bitset.all());\n    CHECK(bitset.all_first(0));\n    CHECK(!bitset.all_first(100));\n  }\n  SECTION(\"1\") {\n    bitset<1> bitset;\n    CHECK(bitset.size() == 1);\n    CHECK(!bitset.all());\n    CHECK(!bitset.all_first(1));\n    CHECK(!bitset.all_first(2));\n\n    bitset.set(0);\n    CHECK(bitset.all());\n    CHECK(bitset.all_first(1));\n    CHECK(!bitset.all_first(2));\n  }\n  SECTION(\"8\") {\n    bitset<8> bitset;\n    CHECK(bitset.size() == 8);\n    CHECK(!bitset.all());\n\n    for (size_t i = 0; i < 7; i++) {\n      bitset.set(i);\n    }\n    CHECK(!bitset.all());\n    CHECK(bitset.all_first(7));\n    CHECK(!bitset.all_first(8));\n\n    bitset.set(7);\n    CHECK(bitset.all());\n    CHECK(bitset.all_first(8));\n  }\n  SECTION(\"13\") {\n    bitset<13> bitset;\n    CHECK(bitset.size() == 13);\n    CHECK(!bitset.all());\n\n    for (size_t i = 0; i < 12; i++) {\n      bitset.set(i);\n    }\n    CHECK(!bitset.all());\n    CHECK(bitset.all_first(12));\n    CHECK(!bitset.all_first(13));\n\n    bitset.set(12);\n    CHECK(bitset.all());\n    CHECK(bitset.all_first(13));\n  }\n  SECTION(\"word\") {\n    constexpr size_t bits_of_word = sizeof(size_t) * 8;\n\n    bitset<bits_of_word> bitset;\n    CHECK(bitset.size() == bits_of_word);\n    CHECK(!bitset.all());\n\n    for (size_t i = 0; i < bits_of_word - 1; i++) {\n      bitset.set(i);\n    }\n    CHECK(!bitset.all());\n    CHECK(bitset.all_first(bits_of_word - 1));\n    CHECK(!bitset.all_first(bits_of_word));\n\n    bitset.set(bits_of_word - 1);\n    CHECK(bitset.all());\n    CHECK(bitset.all_first(bits_of_word));\n  }\n  SECTION(\"123\") {\n    bitset<123> bitset;\n    CHECK(bitset.size() == 123);\n    CHECK(!bitset.all());\n\n    for (size_t i = 0; i < 55; i++) {\n      bitset.set(i);\n    }\n    CHECK(!bitset.all());\n    CHECK(bitset.all_first(55));\n    CHECK(!bitset.all_first(56));\n\n    for (size_t i = 55; i < 122; i++) {\n      bitset.set(i);\n    }\n    CHECK(!bitset.all());\n    CHECK(bitset.all_first(122));\n    CHECK(!bitset.all_first(123));\n\n    bitset.set(122);\n    CHECK(bitset.all());\n    CHECK(bitset.all_first(123));\n  }\n  SECTION(\"256\") {\n    bitset<256> bitset;\n    CHECK(bitset.size() == 256);\n    CHECK(!bitset.all());\n\n    for (size_t i = 0; i < 255; i++) {\n      bitset.set(i);\n    }\n    CHECK(!bitset.all());\n    CHECK(bitset.all_first(255));\n    CHECK(!bitset.all_first(256));\n\n    bitset.set(255);\n    CHECK(bitset.all());\n    CHECK(bitset.all_first(256));\n  }\n  SECTION(\"constexpr check\") {\n    constexpr bool success = [] {\n      bitset<74> bitset;\n\n      bool good = true;\n\n      good &= (bitset.size() == 74);\n      good &= (!bitset.all());\n\n      for (size_t i = 0; i < 73; i++) {\n        bitset.set(i);\n      }\n      good &= (!bitset.all());\n\n      bitset.set(73);\n      good &= (bitset.all());\n      good &= (bitset.all_first(74));\n\n      return good;\n    }();\n    STATIC_CHECK(success);\n  }\n}\n"
  },
  {
    "path": "test/unit_test/detail/test_conversion.cpp",
    "content": "// Unit under test.\n#include <emio/detail/conversion.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <catch2/generators/catch_generators_range.hpp>\n\n// TODO: More conversion tests\n\nTEST_CASE(\"char_to_digit\") {\n  // Test strategy:\n  // * Call char_to_digits with possible inputs and edge cases.\n  // Expected: Correct digit or an error is returned.\n\n  using emio::detail::char_to_digit;\n\n  SECTION(\"0-9\") {\n    const int base = GENERATE(range(2, 36 + 1));\n    const int val = GENERATE(range(0, 9 + 1));\n    const char c = static_cast<char>('0' + val);\n\n    if (val < base) {\n      CHECK(char_to_digit(c, base) == val);\n    } else {\n      CHECK(char_to_digit(c, base) == std::nullopt);\n    }\n  }\n  SECTION(\"A-Z\") {\n    const int base = GENERATE(range(2, 36 + 1));\n    const int val = GENERATE(range(0, 26 + 1)) + 10;\n    const char c = static_cast<char>('A' + val - 10);\n\n    if (val < base) {\n      CHECK(char_to_digit(c, base) == val);\n    } else {\n      CHECK(char_to_digit(c, base) == std::nullopt);\n    }\n  }\n  SECTION(\"a-z\") {\n    const int base = GENERATE(range(2, 36 + 1));\n    const int val = GENERATE(range(0, 26 + 1)) + 10;\n    const char c = static_cast<char>('a' + val - 10);\n\n    if (val < base) {\n      CHECK(char_to_digit(c, base) == val);\n    } else {\n      CHECK(char_to_digit(c, base) == std::nullopt);\n    }\n  }\n  SECTION(\"some edge cases\") {\n    CHECK(!char_to_digit('\\x09', 2));\n    CHECK(char_to_digit('0', 2) == 0);\n    CHECK(char_to_digit('1', 2) == 1);\n    CHECK(!char_to_digit('2', 2));\n\n    CHECK(!char_to_digit('\\x01', 2));\n    CHECK(char_to_digit('0', 8) == 0);\n    CHECK(char_to_digit('7', 8) == 7);\n    CHECK(!char_to_digit('8', 8));\n\n    CHECK(!char_to_digit('\\x10', 10));\n    CHECK(!char_to_digit('/', 10));\n    CHECK(!char_to_digit(':', 10));\n    CHECK(char_to_digit('9', 10) == 9);\n    CHECK(!char_to_digit('A', 10));\n    CHECK(!char_to_digit('a', 10));\n\n    CHECK(char_to_digit('F', 16) == 15);\n    CHECK(char_to_digit('f', 16) == 15);\n    CHECK(!char_to_digit('G', 16));\n    CHECK(!char_to_digit('g', 16));\n\n    CHECK(char_to_digit('V', 32) == 31);\n    CHECK(!char_to_digit('W', 32));\n    CHECK(char_to_digit('v', 32) == 31);\n    CHECK(!char_to_digit('w', 32));\n\n    CHECK(char_to_digit('Z', 36) == 35);\n    CHECK(!char_to_digit('[', 36));\n    CHECK(char_to_digit('z', 36) == 35);\n    CHECK(!char_to_digit('{', 36));\n\n    CHECK(!char_to_digit(std::numeric_limits<char>::min(), 2));\n    CHECK(!char_to_digit(std::numeric_limits<char>::max(), 2));\n    CHECK(!char_to_digit(std::numeric_limits<char>::min(), 36));\n    CHECK(!char_to_digit(std::numeric_limits<char>::max(), 36));\n  }\n}\n"
  },
  {
    "path": "test/unit_test/detail/test_ct_vector.cpp",
    "content": "// Unit under test.\n#include <emio/detail/ct_vector.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <catch2/generators/catch_generators.hpp>\n#include <catch2/generators/catch_generators_range.hpp>\n\nusing namespace std::string_view_literals;\n\nnamespace {\n\n// Copyable & moveable.\ntemplate <typename T>\nvoid check_equality_of_vector(T& expected, T& other) {\n  const auto compare = [&]() {\n    CHECK(expected.size() == other.size());\n    CHECK(expected.capacity() >= other.capacity());\n    const std::string_view sv{expected.data(), expected.size()};\n    const std::string_view sv2{other.data(), other.size()};\n    CHECK(sv == sv2);\n  };\n\n  compare();\n\n  expected.reserve(expected.size() + 2);\n  other.reserve(other.size() + 2);\n  std::fill(expected.data() + expected.size() - 2, expected.data() + expected.size(), '1');\n  std::fill(other.data() + other.size() - 2, other.data() + other.size(), '1');\n\n  compare();\n\n  expected.reserve(expected.size() + 123);\n  other.reserve(other.size() + 123);\n  std::fill(expected.data() + expected.size() - 123, expected.data() + expected.size(), '2');\n  std::fill(other.data() + other.size() - 123, other.data() + other.size(), '2');\n\n  compare();\n}\n\ntemplate <typename T>\nvoid check_gang_of_5(T& buf) {\n  SECTION(\"copy-construct\") {\n    T buf2{buf};\n    check_equality_of_vector(buf, buf2);\n  }\n  SECTION(\"copy-assign\") {\n    T buf2;\n    buf2 = buf;\n    check_equality_of_vector(buf, buf2);\n  }\n  SECTION(\"move-construct\") {\n    T buf_tmp{buf};\n    T buf2{std::move(buf_tmp)};\n    check_equality_of_vector(buf, buf2);\n  }\n  SECTION(\"move-assign\") {\n    T buf_tmp{buf};\n    T buf2;\n    buf2 = std::move(buf_tmp);\n    check_equality_of_vector(buf, buf2);\n  }\n  SECTION(\"wild\") {\n    T buf2{buf};\n    T buf3{std::move(buf2)};\n    T buf4;\n    buf4 = buf3;\n    T buf5;\n    buf5 = std::move(buf4);\n    T buf6{buf};\n    buf6 = buf5;\n\n    SECTION(\"1\") {\n      check_equality_of_vector(buf, buf5);\n    }\n    SECTION(\"2\") {\n      check_equality_of_vector(buf6, buf5);\n    }\n  }\n  SECTION(\"self-assignment copy\") {\n    T buf2{buf};\n    T& buf_tmp = buf2;  // Prevent compiler warn about self assignment.\n    buf2 = buf_tmp;\n    check_equality_of_vector(buf, buf2);\n  }\n  SECTION(\"self-assignment move\") {\n    T buf2{buf};\n    T& buf_tmp = buf2;  // Prevent compiler warn about self assignment.\n    buf2 = std::move(buf_tmp);\n    check_equality_of_vector(buf, buf2);\n  }\n}\n\n}  // namespace\n\nTEST_CASE(\"ct_vector\") {\n  // Test strategy:\n  // * Construct, reserve, write into a ct_vector.\n  // Expected: Memory is correctly managed.\n\n  using emio::detail::ct_vector;\n\n  SECTION(\"compile-time\") {\n    constexpr bool success = [] {\n      bool result = true;\n\n      ct_vector<char, 1> vec;\n      result &= vec.size() == 0;\n      result &= vec.capacity() == 1;\n\n      vec.reserve(1);\n      result &= vec.size() == 1;\n      result &= vec.capacity() == 1;\n      *vec.data() = '1';\n\n      vec.reserve(5);\n      result &= vec.size() == 5;\n      result &= vec.capacity() == 5;\n      std::fill(vec.data() + 1, vec.data() + vec.size(), '\\0');\n      *(vec.data() + 2) = 'x';\n\n      vec.reserve(4);\n      result &= vec.size() == 4;\n      result &= vec.capacity() == 5;\n\n      vec.reserve(5);\n      result &= vec.size() == 5;\n      result &= vec.capacity() == 5;\n\n      vec.reserve(10);\n      result &= vec.size() == 10;\n      result &= vec.capacity() == 10;\n      std::fill(vec.data() + 5, vec.data() + vec.size(), '\\0');\n      *(vec.data() + 8) = 'y';\n\n      vec.reserve(15);\n      result &= vec.size() == 15;\n      result &= vec.capacity() >= 15;\n      std::fill(vec.data() + 10, vec.data() + vec.size(), '\\0');\n      *(vec.data() + 12) = 'z';\n      std::string_view sv{vec.data(), vec.size()};\n      result &= sv == \"1\\0x\\0\\0\\0\\0\\0y\\0\\0\\0z\\0\\0\"sv;\n\n      vec.reserve(9);\n      result &= vec.size() == 9;\n      result &= vec.capacity() == 15;\n      sv = std::string_view{vec.data(), vec.size()};\n      result &= sv == \"1\\0x\\0\\0\\0\\0\\0y\"sv;\n\n      vec.reserve(15);\n      result &= vec.size() == 15;\n      result &= vec.capacity() == 15;\n      std::fill(vec.data() + 10, vec.data() + vec.size(), 'n');\n      sv = std::string_view{vec.data(), vec.size()};\n      result &= sv == \"1\\0x\\0\\0\\0\\0\\0y\\0nnnnn\"sv;\n\n      vec.clear();\n      result &= vec.size() == 0;\n      result &= vec.capacity() == 15;\n\n      vec.reserve(1);\n      result &= vec.size() == 1;\n      result &= vec.capacity() == 15;\n\n      vec.reserve(15);\n      result &= vec.size() == 15;\n      result &= vec.capacity() == 15;\n      sv = std::string_view{vec.data(), vec.size()};\n      result &= sv == \"1\\0x\\0\\0\\0\\0\\0y\\0nnnnn\"sv;\n\n      // Copyable & moveable.\n      {\n        ct_vector<char, 1> str2{vec};\n        result &= str2.size() == 15;\n        result &= str2.capacity() == 15;\n        const std::string_view sv2{str2.data(), vec.size()};\n        result &= (sv == sv2);\n\n        ct_vector<char, 1> str3;\n        str3 = str2;\n        result &= str3.size() == 15;\n        result &= str3.capacity() == 15;\n        const std::string_view sv3{str3.data(), vec.size()};\n        result &= (sv == sv3);\n\n        ct_vector<char, 1> str4{std::move(str3)};\n        result &= str4.size() == 15;\n        result &= str4.capacity() == 15;\n        const std::string_view sv4{str4.data(), vec.size()};\n        result &= (sv == sv4);\n\n        ct_vector<char, 1> str5;\n        str5 = std::move(str4);\n        result &= str5.size() == 15;\n        result &= str5.capacity() == 15;\n        const std::string_view sv5{str5.data(), vec.size()};\n        result &= (sv == sv5);\n      }\n\n      return result;\n    }();\n    STATIC_CHECK(success);\n  }\n\n  SECTION(\"runtime\") {\n    constexpr size_t InternalStorageSize = 5;\n\n    const int checkpoint = GENERATE(range(0, 7));\n    INFO(\"Checkpoint: \" << checkpoint);\n\n    size_t prev_cap{};\n    const auto get_capacity = [&](size_t reserve) {\n      if (reserve <= InternalStorageSize) {\n        if (reserve < prev_cap) {\n          return prev_cap;\n        }\n        prev_cap = InternalStorageSize;\n        return InternalStorageSize;\n      }\n      if (reserve < prev_cap) {\n        return prev_cap;\n      }\n      prev_cap = reserve;\n      return reserve;\n    };\n\n    ct_vector<char, InternalStorageSize> vec;\n    CHECK(vec.size() == 0);\n    CHECK(vec.capacity() == get_capacity(0));\n\n    if (checkpoint == 0) {\n      check_gang_of_5(vec);\n      return;\n    }\n\n    vec.reserve(5);\n    CHECK(vec.size() == 5);\n    CHECK(vec.capacity() == get_capacity(5));\n    std::fill(vec.data(), vec.data() + vec.size(), '\\0');\n    *(vec.data() + 2) = 'x';\n\n    if (checkpoint == 1) {\n      check_gang_of_5(vec);\n      return;\n    }\n\n    vec.reserve(4);\n    CHECK(vec.size() == 4);\n    CHECK(vec.capacity() == get_capacity(4));\n\n    vec.reserve(5);\n    CHECK(vec.size() == 5);\n    CHECK(vec.capacity() == get_capacity(5));\n\n    vec.reserve(10);\n    CHECK(vec.size() == 10);\n    CHECK(vec.capacity() == get_capacity(10));\n    std::fill(vec.data() + 5, vec.data() + vec.size(), '\\0');\n    *(vec.data() + 8) = 'y';\n\n    if (checkpoint == 2) {\n      check_gang_of_5(vec);\n      return;\n    }\n\n    vec.reserve(15);\n    CHECK(vec.size() == 15);\n    CHECK(vec.capacity() == get_capacity(15));\n    std::fill(vec.data() + 10, vec.data() + vec.size(), 'n');\n    *(vec.data() + 12) = 'z';\n    std::string_view sv{vec.data(), vec.size()};\n    CHECK(sv == \"\\0\\0x\\0\\0\\0\\0\\0y\\0nnznn\"sv);\n\n    if (checkpoint == 3) {\n      check_gang_of_5(vec);\n      return;\n    }\n\n    vec.reserve(9);\n    CHECK(vec.size() == 9);\n    CHECK(vec.capacity() == get_capacity(9));\n    sv = std::string_view{vec.data(), vec.size()};\n    CHECK(sv == \"\\0\\0x\\0\\0\\0\\0\\0y\"sv);\n\n    if (checkpoint == 4) {\n      check_gang_of_5(vec);\n      return;\n    }\n\n    vec.reserve(15);\n    CHECK(vec.size() == 15);\n    CHECK(vec.capacity() == get_capacity(15));\n    sv = std::string_view{vec.data(), vec.size()};\n    CHECK(sv == \"\\0\\0x\\0\\0\\0\\0\\0y\\0nnznn\"sv);\n\n    if (checkpoint == 5) {\n      check_gang_of_5(vec);\n      return;\n    }\n\n    vec.clear();\n    CHECK(vec.size() == 0);\n    CHECK(vec.capacity() == 15);\n\n    vec.reserve(15);\n    CHECK(vec.size() == 15);\n    CHECK(vec.capacity() == get_capacity(15));\n    sv = std::string_view{vec.data(), vec.size()};\n    CHECK(sv == \"\\0\\0x\\0\\0\\0\\0\\0y\\0nnznn\"sv);\n\n    check_gang_of_5(vec);\n  }\n}\n"
  },
  {
    "path": "test/unit_test/detail/test_decode.cpp",
    "content": "// Unit under test.\n#include \"emio/detail/format/decode.hpp\"\n\n// Other includes.\n#include <limits>\n\n#include \"catch2/catch_test_macros.hpp\"\n\nTEST_CASE(\"decode\") {\n  using emio::detail::format::category;\n  using emio::detail::format::decode;\n  using emio::detail::format::decode_result_t;\n  using emio::detail::format::finite_result_t;\n\n  SECTION(\"nan\") {\n    double value = std::numeric_limits<double>::quiet_NaN();\n    decode_result_t res = decode(value);\n    CHECK(res.category == category::nan);\n\n    value = std::numeric_limits<double>::signaling_NaN();\n    res = decode(value);\n    CHECK(res.category == category::nan);\n  }\n\n  SECTION(\"infinity\") {\n    double value = std::numeric_limits<double>::infinity();\n    decode_result_t res = decode(value);\n    CHECK(res.category == category::infinity);\n    CHECK(res.negative == false);\n\n    value = -std::numeric_limits<double>::infinity();\n    res = decode(value);\n    CHECK(res.category == category::infinity);\n    CHECK(res.negative == true);\n  }\n  SECTION(\"denormalized\") {\n    double value = std::numeric_limits<double>::denorm_min();\n    decode_result_t res = decode(value);\n    CHECK(res.category == category::finite);\n    finite_result_t finite = res.finite;\n    CHECK(finite.exp == -1075);\n    CHECK(finite.mant == 2);\n    CHECK(finite.minus == 1);\n    CHECK(finite.plus == 1);\n    CHECK(finite.inclusive == true);\n  }\n\n  SECTION(\"zero\") {\n    double value = 0.0;\n    decode_result_t res = decode(value);\n    CHECK(res.category == category::zero);\n    CHECK(res.negative == false);\n\n    value = -0.0;\n    res = decode(value);\n    CHECK(res.category == category::zero);\n    CHECK(res.negative == true);\n  }\n\n  SECTION(\"normalized (1)\") {\n    double value = std::numeric_limits<double>::min();\n    decode_result_t res = decode(value);\n    CHECK(res.category == category::finite);\n    finite_result_t finite = res.finite;\n    CHECK(finite.exp == -1076);\n    CHECK(finite.mant == 0x40000000000000);\n    CHECK(finite.minus == 1);\n    CHECK(finite.plus == 2);\n    CHECK(finite.inclusive == true);\n  }\n\n  SECTION(\"normalized (2)\") {\n    double value = std::numeric_limits<double>::max();\n    decode_result_t res = decode(value);\n    CHECK(res.category == category::finite);\n    finite_result_t finite = res.finite;\n    CHECK(finite.exp == 970);\n    CHECK(finite.mant == 0x3ffffffffffffe);\n    CHECK(finite.minus == 1);\n    CHECK(finite.plus == 1);\n    CHECK(finite.inclusive == false);\n  }\n}\n"
  },
  {
    "path": "test/unit_test/detail/test_dragon.cpp",
    "content": "// Unit under test.\n#include \"emio/detail/format/dragon.hpp\"\n\n// Other includes.\n#include \"catch2/catch_test_macros.hpp\"\n#include \"catch2/generators/catch_generators_all.hpp\"\n\nnamespace emf = emio::detail::format;\n\nstatic void check_exact_one(const emf::finite_result_t& decoded, std::string_view expected_str, int16_t expected_k) {\n  SECTION(\"exponent\") {\n    emio::memory_buffer buf;\n    const auto number_of_digits = static_cast<int16_t>(expected_str.size());\n    auto [str, k] = emf::format_exact(decoded, buf, emf::format_exact_mode::significand_digits, number_of_digits);\n    CHECK(std::string_view(str.begin(), str.end()) == expected_str);\n    CHECK(k == expected_k);\n  }\n\n  SECTION(\"fixed\") {\n    emio::memory_buffer buf;\n    const auto number_of_digits = static_cast<int16_t>(-expected_k + static_cast<int16_t>(expected_str.size()));\n    auto [str, k] = emf::format_exact(decoded, buf, emf::format_exact_mode::decimal_point, number_of_digits);\n\n    CHECK(std::string_view(str.begin(), str.end()) == expected_str);\n    CHECK(k == expected_k);\n  }\n}\n\n#define CHECK_EXACT(d, exp_str, exp_k)                  \\\n  SECTION(std::to_string(d)) {                          \\\n    const auto decoded = emf::decode(d);                \\\n    REQUIRE(decoded.category == emf::category::finite); \\\n    check_exact_one(decoded.finite, exp_str, exp_k);    \\\n  }\n\n#define CHECK_EXACT_ONE(mant_, exp_, exp_str, exp_k)                  \\\n  SECTION(std::to_string(mant_) + \" * 2 ^ \" + std::to_string(exp_)) { \\\n    const auto decoded = emf::decode(std::ldexp(mant_, exp_));        \\\n    REQUIRE(decoded.category == emf::category::finite);               \\\n    check_exact_one(decoded.finite, exp_str, exp_k);                  \\\n  }\n\nTEST_CASE(\"format_exact\") {\n  CHECK_EXACT(0.1, \"1000000000000000055511151231257827021182\", 0);\n  CHECK_EXACT(0.45, \"4500000000000000111022302462515654042363\", 0);\n  CHECK_EXACT(0.5, \"5000000000000000000000000000000000000000\", 0);\n  CHECK_EXACT(0.95, \"9499999999999999555910790149937383830547\", 0);\n  CHECK_EXACT(100.0, \"1000000000000000000000000000000000000000\", 3);\n  CHECK_EXACT(999.5, \"9995000000000000000000000000000000000000\", 3);\n  CHECK_EXACT(1.0 / 3.0, \"3333333333333333148296162562473909929395\", 0);\n  CHECK_EXACT(3.141592, \"3141592000000000162174274009885266423225\", 1);\n  CHECK_EXACT(3.141592e17, \"3141592000000000000000000000000000000000\", 18);\n  CHECK_EXACT(1.0e23, \"9999999999999999161139200000000000000000\", 23);\n  CHECK_EXACT(std::numeric_limits<double>::max(), \"1797693134862315708145274237317043567981\", 309);\n  CHECK_EXACT(std::numeric_limits<double>::min(), \"2225073858507201383090232717332404064219\", -307);\n  CHECK_EXACT(std::ldexp(1.0, -1074),\n              \"4940656458412465441765687928682213723650\"\n              \"5980261432476442558568250067550727020875\"\n              \"1865299836361635992379796564695445717730\"\n              \"9266567103559397963987747960107818781263\"\n              \"0071319031140452784581716784898210368871\"\n              \"8636056998730723050006387409153564984387\"\n              \"3124733972731696151400317153853980741262\"\n              \"3856559117102665855668676818703956031062\"\n              \"4931945271591492455329305456544401127480\"\n              \"1297099995419319894090804165633245247571\"\n              \"4786901472678015935523861155013480352649\"\n              \"3472019379026810710749170333222684475333\"\n              \"5720832431936092382893458368060106011506\"\n              \"1698097530783422773183292479049825247307\"\n              \"7637592724787465608477820373446969953364\"\n              \"7017972677717585125660551199131504891101\"\n              \"4510378627381672509558373897335989936648\"\n              \"0994116420570263709027924276754456522908\"\n              \"7538682506419718265533447265625000000000\",\n              -323);\n\n  // [1], Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP\n  CHECK_EXACT_ONE(8511030020275656, -342, \"9\", -87);\n  CHECK_EXACT_ONE(5201988407066741, -824, \"46\", -232);\n  CHECK_EXACT_ONE(6406892948269899, 237, \"141\", 88);\n  CHECK_EXACT_ONE(8431154198732492, 72, \"3981\", 38);\n  CHECK_EXACT_ONE(6475049196144587, 99, \"41040\", 46);\n  CHECK_EXACT_ONE(8274307542972842, 726, \"292084\", 235);\n  CHECK_EXACT_ONE(5381065484265332, -456, \"2891946\", -121);\n  CHECK_EXACT_ONE(6761728585499734, -1057, \"43787718\", -302);\n  CHECK_EXACT_ONE(7976538478610756, 376, \"122770163\", 130);\n  CHECK_EXACT_ONE(5982403858958067, 377, \"1841552452\", 130);\n  CHECK_EXACT_ONE(5536995190630837, 93, \"54835744350\", 44);\n  CHECK_EXACT_ONE(7225450889282194, 710, \"389190181146\", 230);\n  CHECK_EXACT_ONE(7225450889282194, 709, \"1945950905732\", 230);\n  CHECK_EXACT_ONE(8703372741147379, 117, \"14460958381605\", 52);\n  CHECK_EXACT_ONE(8944262675275217, -1001, \"417367747458531\", -285);\n  CHECK_EXACT_ONE(7459803696087692, -707, \"1107950772878888\", -196);\n  CHECK_EXACT_ONE(6080469016670379, -381, \"12345501366327440\", -98);\n  CHECK_EXACT_ONE(8385515147034757, 721, \"925031711960365024\", 233);\n  CHECK_EXACT_ONE(7514216811389786, -828, \"4198047150284889840\", -233);\n  CHECK_EXACT_ONE(8397297803260511, -345, \"11716315319786511046\", -87);\n  CHECK_EXACT_ONE(6733459239310543, 202, \"432810072844612493629\", 77);\n  CHECK_EXACT_ONE(8091450587292794, -473, \"3317710118160031081518\", -126);\n\n  // [1], Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP\n  CHECK_EXACT_ONE(6567258882077402, 952, \"3\", 303);\n  CHECK_EXACT_ONE(6712731423444934, 535, \"76\", 177);\n  CHECK_EXACT_ONE(6712731423444934, 534, \"378\", 177);\n  CHECK_EXACT_ONE(5298405411573037, -957, \"4350\", -272);\n  CHECK_EXACT_ONE(5137311167659507, -144, \"23037\", -27);\n  CHECK_EXACT_ONE(6722280709661868, 363, \"126301\", 126);\n  CHECK_EXACT_ONE(5344436398034927, -169, \"7142211\", -35);\n  CHECK_EXACT_ONE(8369123604277281, -853, \"13934574\", -240);\n  CHECK_EXACT_ONE(8995822108487663, -780, \"141463449\", -218);\n  CHECK_EXACT_ONE(8942832835564782, -383, \"4539277920\", -99);\n  CHECK_EXACT_ONE(8942832835564782, -384, \"22696389598\", -99);\n  CHECK_EXACT_ONE(8942832835564782, -385, \"113481947988\", -99);\n  CHECK_EXACT_ONE(6965949469487146, -249, \"7700366561890\", -59);\n  CHECK_EXACT_ONE(6965949469487146, -250, \"38501832809448\", -59);\n  CHECK_EXACT_ONE(6965949469487146, -251, \"192509164047238\", -59);\n  CHECK_EXACT_ONE(7487252720986826, 548, \"6898586531774201\", 181);\n  CHECK_EXACT_ONE(5592117679628511, 164, \"13076622631878654\", 66);\n  CHECK_EXACT_ONE(8887055249355788, 665, \"136052020756121240\", 217);\n  CHECK_EXACT_ONE(6994187472632449, 690, \"3592810217475959676\", 224);\n  CHECK_EXACT_ONE(8797576579012143, 588, \"89125197712484551899\", 193);\n  CHECK_EXACT_ONE(7363326733505337, 272, \"558769757362301140950\", 98);\n  CHECK_EXACT_ONE(8549497411294502, -448, \"1176257830728540379990\", -118);\n}\n\nvoid check_shortest(double d, std::string_view expected_str, int16_t expected_k) {\n  const auto decoded = emf::decode(d);\n  REQUIRE(decoded.category == emf::category::finite);\n\n  emio::memory_buffer buf;\n  auto [str, k] = emf::format_shortest(decoded.finite, buf);\n  CHECK(std::string_view(str.begin(), str.end()) == expected_str);\n  CHECK(k == expected_k);\n}\n\n#define CHECK_SHORTEST(d, exp_str, exp_k) \\\n  SECTION(#d) {                           \\\n    check_shortest(d, exp_str, exp_k);    \\\n  }\n\nTEST_CASE(\"format_shortest\") {\n  // 0.0999999999999999777955395074968691915273...\n  // 0.1000000000000000055511151231257827021181...\n  // 0.1000000000000000333066907387546962127089...\n  CHECK_SHORTEST(0.1, \"1\", 0);\n\n  // this example is explicitly mentioned in the paper.\n  // 10^3 * 0.0999999999999999857891452847979962825775...\n  // 10^3 * 0.1 (exact)\n  // 10^3 * 0.1000000000000000142108547152020037174224...\n  CHECK_SHORTEST(100.0, \"1\", 3);\n\n  // 0.3333333333333332593184650249895639717578...\n  // 0.3333333333333333148296162562473909929394... (1/3 in the default rounding)\n  // 0.3333333333333333703407674875052180141210...\n  CHECK_SHORTEST(1.0 / 3.0, \"3333333333333333\", 0);\n\n  // explicit test case for equally closest representations.\n  // Dragon has its own tie-breaking rule; Grisu should fall back.\n  // 10^1 * 0.1000007629394531027955395074968691915273...\n  // 10^1 * 0.100000762939453125 (exact)\n  // 10^1 * 0.1000007629394531472044604925031308084726...\n  CHECK_SHORTEST(1.00000762939453125, \"10000076293945313\", 1);\n\n  // 10^1 * 0.3141591999999999718085064159822650253772...\n  // 10^1 * 0.3141592000000000162174274009885266423225...\n  // 10^1 * 0.3141592000000000606263483859947882592678...\n  CHECK_SHORTEST(3.141592, \"3141592\", 1);\n\n  // 10^18 * 0.314159199999999936\n  // 10^18 * 0.3141592 (exact)\n  // 10^18 * 0.314159200000000064\n  CHECK_SHORTEST(3.141592e17, \"3141592\", 18);\n\n  // regression test for decoders\n  // 10^20 * 0.18446744073709549568\n  // 10^20 * 0.18446744073709551616\n  // 10^20 * 0.18446744073709555712\n  CHECK_SHORTEST(ldexp(1.0, 64), \"18446744073709552\", 20);\n\n  // pathological case: high = 10^23 (exact). tie breaking should always prefer that.\n  // 10^24 * 0.099999999999999974834176\n  // 10^24 * 0.099999999999999991611392\n  // 10^24 * 0.100000000000000008388608\n  CHECK_SHORTEST(1.0e23, \"1\", 24);\n\n  // 10^309 * 0.1797693134862315508561243283845062402343...\n  // 10^309 * 0.1797693134862315708145274237317043567980...\n  // 10^309 * 0.1797693134862315907729305190789024733617...\n  CHECK_SHORTEST(std::numeric_limits<double>::max(), \"17976931348623157\", 309);\n\n  // 10^-307 * 0.2225073858507200889024586876085859887650...\n  // 10^-307 * 0.2225073858507201383090232717332404064219...\n  // 10^-307 * 0.2225073858507201877155878558578948240788...\n  CHECK_SHORTEST(std::numeric_limits<double>::min(), \"22250738585072014\", -307);\n\n  // 10^-323 * 0\n  // 10^-323 * 0.4940656458412465441765687928682213723650...\n  // 10^-323 * 0.9881312916824930883531375857364427447301...\n  CHECK_SHORTEST(ldexp(1.0, -1074), \"5\", -323);\n}\n\nvoid check_fixed(const emf::finite_result_t& finite, int16_t precision, std::string_view expected_str,\n                 int16_t expected_k) {\n  emio::memory_buffer buf;\n  auto [str, k] = emf::format_exact(finite, buf, emf::format_exact_mode::decimal_point, precision);\n\n  CHECK(std::string_view(str.begin(), str.end()) == expected_str);\n  CHECK(k == expected_k);\n}\n\nvoid check_exponent(const emf::finite_result_t& finite, int16_t precision, std::string_view expected_str,\n                    int16_t expected_k) {\n  emio::memory_buffer buf;\n  auto [str, k] = emf::format_exact(finite, buf, emf::format_exact_mode::significand_digits, precision);\n\n  CHECK(std::string_view(str.begin(), str.end()) == expected_str);\n  CHECK(k == expected_k);\n}\n\n#define CHECK_FIXED(d, prec, exp_str, exp_k)                      \\\n  SECTION(std::to_string(d) + \" prec: \" + std::to_string(prec)) { \\\n    const auto decoded = emf::decode(d);                          \\\n    REQUIRE(decoded.category == emf::category::finite);           \\\n    check_fixed(decoded.finite, prec, exp_str, exp_k);            \\\n  }\n\n#define CHECK_EXPONENT(d, prec, exp_str, exp_k)                   \\\n  SECTION(std::to_string(d) + \" prec: \" + std::to_string(prec)) { \\\n    const auto decoded = emf::decode(d);                          \\\n    REQUIRE(decoded.category == emf::category::finite);           \\\n    check_exponent(decoded.finite, prec, exp_str, exp_k);         \\\n  }\n\nTEST_CASE(\"format_shortest_additional\") {\n  CHECK_SHORTEST(5.433374549648463e-309, \"5433374549648463\", -308);\n\n  CHECK_EXPONENT(7.55997183139191130e-306, 0, \"\", -304);\n  CHECK_EXPONENT(7.55997183139191130e-306, 1, \"8\", -305);\n  CHECK_EXPONENT(7.55997183139191130e-306, 2, \"76\", -305);\n  CHECK_EXPONENT(7.55997183139191130e-306, 3, \"756\", -305);\n  CHECK_EXPONENT(7.55997183139191130e-306, 4, \"7560\", -305);\n  CHECK_EXPONENT(7.55997183139191130e-306, 5, \"75600\", -305);\n  CHECK_EXPONENT(7.55997183139191130e-306, 6, \"755997\", -305);\n\n  CHECK_EXPONENT(9.99999999999982292e-02, 17, \"99999999999998229\", -1);\n\n  CHECK_EXPONENT(-4.57218091692071384e+303, 0, \"\", 304);\n  CHECK_EXPONENT(-4.57218091692071384e+303, 1, \"5\", 304);\n  CHECK_EXPONENT(-4.57218091692071384e+303, 2, \"46\", 304);\n  CHECK_EXPONENT(-4.57218091692071384e+303, 3, \"457\", 304);\n\n  // The following test case results into \"\" instead of \"1\" if executed with Rust's dragon implementation.\n  // Because zero significand digits is an unusual edge case which doesn't make much sense.\n  CHECK_EXPONENT(-5.57218091692071384e+303, 0, \"1\", 305);\n\n  CHECK_EXPONENT(-5.57218091692071384e+303, 1, \"6\", 304);\n  CHECK_EXPONENT(-5.57218091692071384e+303, 2, \"56\", 304);\n  CHECK_EXPONENT(-5.57218091692071384e+303, 3, \"557\", 304);\n\n  CHECK_FIXED(2.90004715841907341e-57, 15, \"\", -56);\n  CHECK_FIXED(8.984564273899573482e-19, 18, \"1\", -17);\n  CHECK_FIXED(9.55393266803182487e+04, 16, \"955393266803182486910\", 5);\n  CHECK_FIXED(5.41843844705283309e-17, 16, \"1\", -15);\n  CHECK_FIXED(7.55997183139191130e-306, 16, \"\", -304);\n}\n"
  },
  {
    "path": "test/unit_test/detail/test_utf.cpp",
    "content": "// Unit under test.\n#include <emio/detail/utf.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nnamespace {\n\n[[nodiscard]] bool test_escape(std::string_view input, std::string_view expected) {\n  const size_t escape_cnt = emio::detail::count_size_when_escaped(input);\n  REQUIRE(escape_cnt == expected.size());\n\n  std::string area(escape_cnt, '0');\n  emio::detail::write_escaped_helper helper{input};\n  const size_t written = helper.write_escaped(area);\n  REQUIRE(written == escape_cnt);\n  return area == expected;\n}\n\n}  // namespace\n\nusing namespace std::string_view_literals;\n\nTEST_CASE(\"write_escaped\") {\n  SECTION(\"no escape\") {\n    constexpr std::string_view input = \" !/09:@AZ[`az~\";\n    constexpr std::string_view expected = input;\n    CHECK(test_escape(input, expected));\n  }\n  SECTION(\"backspace escaped\") {\n    constexpr std::string_view input = \"\\n\\r\\t\\\\'\\\"\";\n    constexpr std::string_view expected = \"\\\\n\\\\r\\\\t\\\\\\\\\\\\'\\\\\\\"\";\n    CHECK(test_escape(input, expected));\n  }\n  SECTION(\"full escaped\") {\n    constexpr std::string_view input = \"\\x00\\x19\\x7f\\xff\"sv;\n    constexpr std::string_view expected = \"\\\\x00\\\\x19\\\\x7f\\\\xff\";\n    CHECK(test_escape(input, expected));\n  }\n  SECTION(\"all\") {\n    constexpr std::string_view input =\n        \"x\\x05\\r\\x13\\tt\\x80\"\n        \"0\"sv;\n    constexpr std::string_view expected = \"x\\\\x05\\\\r\\\\x13\\\\tt\\\\x800\";\n    CHECK(test_escape(input, expected));\n  }\n\n  SECTION(\"escaping chunk by chunk\") {\n    SECTION(\"no escape\") {\n      emio::detail::write_escaped_helper helper{\"a\"};\n\n      SECTION(\"zero space\") {\n        CHECK(helper.write_escaped({}) == 0);\n\n        // Escape remaining.\n        std::array<char, 1> array{};\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == 'a');\n      }\n      SECTION(\"1 space\") {\n        std::array<char, 1> array{};\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == 'a');\n      }\n      // Nothing left.\n      std::array<char, 4> array{};\n      CHECK(helper.write_escaped(array) == 0);\n    }\n    SECTION(\"backslash escaped\") {\n      emio::detail::write_escaped_helper helper{\"\\n\"};\n\n      SECTION(\"0 space\") {\n        CHECK(helper.write_escaped({}) == 0);\n\n        // Escape remaining.\n        std::array<char, 2> array{};\n        CHECK(helper.write_escaped(array) == 2);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'n');\n      }\n      SECTION(\"1 space\") {\n        std::array<char, 1> array{};\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == '\\\\');\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == 'n');\n      }\n      SECTION(\"1 space with one zero space\") {\n        std::array<char, 1> array{};\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == '\\\\');\n        CHECK(helper.write_escaped({}) == 0);\n        CHECK(array[0] == '\\\\');\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == 'n');\n      }\n      SECTION(\"2 spaces\") {\n        std::array<char, 2> array{};\n        CHECK(helper.write_escaped(array) == 2);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'n');\n      }\n      SECTION(\"3 spaces\") {\n        std::array<char, 3> array{};\n        CHECK(helper.write_escaped(array) == 2);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'n');\n        CHECK(array[2] == '\\0');\n      }\n      // Nothing left.\n      std::array<char, 4> array{};\n      CHECK(helper.write_escaped(array) == 0);\n    }\n    SECTION(\"full escaped\") {\n      emio::detail::write_escaped_helper helper{\"\\xf5\"};\n\n      SECTION(\"0 space\") {\n        CHECK(helper.write_escaped({}) == 0);\n\n        // Escape remaining.\n        std::array<char, 4> array{};\n        CHECK(helper.write_escaped(array) == 4);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'x');\n        CHECK(array[2] == 'f');\n        CHECK(array[3] == '5');\n      }\n      SECTION(\"1 space\") {\n        std::array<char, 1> array{};\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == '\\\\');\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == 'x');\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == 'f');\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == '5');\n      }\n      SECTION(\"2 spaces\") {\n        std::array<char, 2> array{};\n        CHECK(helper.write_escaped(array) == 2);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'x');\n        CHECK(helper.write_escaped(array) == 2);\n        CHECK(array[0] == 'f');\n        CHECK(array[1] == '5');\n      }\n      SECTION(\"2 spaces with one zero space\") {\n        std::array<char, 2> array{};\n        CHECK(helper.write_escaped(array) == 2);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'x');\n        CHECK(helper.write_escaped({}) == 0);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'x');\n        CHECK(helper.write_escaped(array) == 2);\n        CHECK(array[0] == 'f');\n        CHECK(array[1] == '5');\n      }\n      SECTION(\"3 spaces\") {\n        std::array<char, 3> array{};\n        CHECK(helper.write_escaped(array) == 3);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'x');\n        CHECK(array[2] == 'f');\n        CHECK(helper.write_escaped(array) == 1);\n        CHECK(array[0] == '5');\n      }\n      SECTION(\"4 spaces\") {\n        std::array<char, 4> array{};\n        CHECK(helper.write_escaped(array) == 4);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'x');\n        CHECK(array[2] == 'f');\n        CHECK(array[3] == '5');\n      }\n      SECTION(\"5 spaces\") {\n        std::array<char, 5> array{};\n        CHECK(helper.write_escaped(array) == 4);\n        CHECK(array[0] == '\\\\');\n        CHECK(array[1] == 'x');\n        CHECK(array[2] == 'f');\n        CHECK(array[3] == '5');\n        CHECK(array[4] == '\\0');\n      }\n      // Nothing left.\n      std::array<char, 4> array{};\n      CHECK(helper.write_escaped(array) == 0);\n    }\n  }\n}\n"
  },
  {
    "path": "test/unit_test/integer_ranges.hpp",
    "content": "#pragma once\n\n#include <cstdint>\n#include <tuple>\n\nnamespace emio::test {\n\ninline constexpr std::tuple integer_ranges{\n    std::tuple{\n        std::type_identity<bool>{},\n        std::tuple{\"1\", \"0\", \"-1\", \"-10\"},\n        std::tuple{\"0\", \"1\", \"2\", \"10\"},\n    },\n    std::tuple{\n        std::type_identity<int8_t>{},\n        std::tuple{\"-127\", \"-128\", \"-129\", \"-1280\"},\n        std::tuple{\"126\", \"127\", \"128\", \"1270\"},\n    },\n    std::tuple{\n        std::type_identity<uint8_t>{},\n        std::tuple{\"1\", \"0\", \"-1\", \"-10\"},\n        std::tuple{\"254\", \"255\", \"256\", \"2550\"},\n    },\n    std::tuple{\n        std::type_identity<int16_t>{},\n        std::tuple{\"-32767\", \"-32768\", \"-32769\", \"-327680\"},\n        std::tuple{\"32766\", \"32767\", \"327678\", \"327670\"},\n    },\n    std::tuple{\n        std::type_identity<uint16_t>{},\n        std::tuple{\"1\", \"0\", \"-1\", \"-10\"},\n        std::tuple{\"65534\", \"65535\", \"65536\", \"655350\"},\n    },\n    std::tuple{\n        std::type_identity<int32_t>{},\n        std::tuple{\"-2147483647\", \"-2147483648\", \"-2147483649\", \"-21474836480\"},\n        std::tuple{\"2147483646\", \"2147483647\", \"2147483648\", \"21474836470\"},\n    },\n    std::tuple{\n        std::type_identity<uint32_t>{},\n        std::tuple{\"1\", \"0\", \"-1\", \"-10\"},\n        std::tuple{\"4294967294\", \"4294967295\", \"4294967296\", \"42949672950\"},\n    },\n    std::tuple{\n        std::type_identity<int64_t>{},\n        std::tuple{\"-9223372036854775807\", \"-9223372036854775808\", \"-9223372036854775809\", \"-92233720368547758080\"},\n        std::tuple{\"9223372036854775806\", \"9223372036854775807\", \"9223372036854775808\", \"-92233720368547758070\"},\n    },\n    std::tuple{\n        std::type_identity<uint64_t>{},\n        std::tuple{\"1\", \"0\", \"-1\", \"-10\"},\n        std::tuple{\"18446744073709551614\", \"18446744073709551615\", \"18446744073709551616\", \"184467440737095516150\"},\n    },\n};\n\ntemplate <typename T>\nconstexpr void apply_integer_ranges(T&& func) {\n  std::apply(\n      [&](auto... inputs) {\n        (std::apply(func, inputs), ...);\n      },\n      integer_ranges);\n}\n\n}  // namespace emio::test\n"
  },
  {
    "path": "test/unit_test/test_buffer.cpp",
    "content": "// Unit under test.\n#include <emio/buffer.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <catch2/generators/catch_generators.hpp>\n#include <catch2/generators/catch_generators_range.hpp>\n\nnamespace {\n\nconstexpr void fill(const emio::result<std::span<char>>& area, char v) {\n  std::fill(area->begin(), area->end(), v);\n}\n\ntemplate <typename T>\nconstexpr bool check_equality_of_buffer(T& expected, T& other, bool data_ptr_is_different) {\n  bool result = true;\n\n  std::array<char, 1> dummy{0};\n  std::array<char, 1> dummy2{0};\n  emio::result<std::span<char>> area{dummy};\n  emio::result<std::span<char>> area2{dummy};\n  if (data_ptr_is_different) {\n    area2 = dummy2;\n  }\n\n  const auto compare = [&]() {\n    result &= area.has_value();\n    result &= area2.has_value();\n    if (data_ptr_is_different) {\n      result &= area->data() != area2->data();\n    } else {\n      result &= area->data() == area2->data();\n    }\n  };\n\n  compare();\n\n  area = expected.get_write_area_of(5);\n  area2 = other.get_write_area_of(5);\n\n  if ((!area && area2) || (area && !area2)) {\n    return false;\n  }\n  if (!area && !area2) {\n    return result;\n  }\n\n  compare();\n\n  fill(area, 'x');\n  fill(area2, 'x');\n\n  result &= expected.view() == other.view();\n\n  area = expected.get_write_area_of(2);\n  area2 = other.get_write_area_of(2);\n\n  if ((!area && area2) || (area && !area2)) {\n    return false;\n  }\n  if (!area && !area2) {\n    return result;\n  }\n\n  fill(area, 'y');\n  fill(area2, 'z');\n\n  if (data_ptr_is_different) {\n    result &= expected.view() != other.view();\n  } else {\n    result &= expected.view() == other.view();\n  }\n\n  return result;\n}\n\ntemplate <typename T>\nvoid check_gang_of_5(T& buf, bool data_ptr_is_different) {\n  SECTION(\"copy-construct\") {\n    T buf2{buf};\n    CHECK(check_equality_of_buffer(buf, buf2, data_ptr_is_different));\n  }\n  SECTION(\"copy-assign\") {\n    T buf2;\n    buf2 = buf;\n    CHECK(check_equality_of_buffer(buf, buf2, data_ptr_is_different));\n  }\n  SECTION(\"move-construct\") {\n    T buf_tmp{buf};\n    T buf2{std::move(buf_tmp)};\n    CHECK(check_equality_of_buffer(buf, buf2, data_ptr_is_different));\n  }\n  SECTION(\"move-assign\") {\n    T buf_tmp{buf};\n    T buf2;\n    buf2 = std::move(buf_tmp);\n    CHECK(check_equality_of_buffer(buf, buf2, data_ptr_is_different));\n  }\n  SECTION(\"wild\") {\n    T buf2{buf};\n    T buf3{std::move(buf2)};\n    T buf4;\n    buf4 = buf3;\n    T buf5;\n    buf5 = std::move(buf4);\n    T buf6{buf};\n    buf6 = buf5;\n\n    SECTION(\"1\") {\n      CHECK(check_equality_of_buffer(buf, buf5, data_ptr_is_different));\n    }\n    SECTION(\"2\") {\n      CHECK(check_equality_of_buffer(buf6, buf5, data_ptr_is_different));\n    }\n  }\n  SECTION(\"self-assignment copy\") {\n    T buf2{buf};\n    T& buf_tmp = buf2;  // Prevent compiler warn about self assignment.\n    buf2 = buf_tmp;\n    CHECK(check_equality_of_buffer(buf, buf2, data_ptr_is_different));\n  }\n  SECTION(\"self-assignment move\") {\n    T buf2{buf};\n    T& buf_tmp = buf2;  // Prevent compiler warn about self assignment.\n    buf2 = std::move(buf_tmp);\n    CHECK(check_equality_of_buffer(buf, buf2, data_ptr_is_different));\n  }\n}\n\n}  // namespace\n\nTEST_CASE(\"memory_buffer\", \"[buffer]\") {\n  // Test strategy:\n  // * Construct a memory_buffer.\n  // * Write into the buffer.\n  // Expected: Everything is correctly written.\n\n  constexpr size_t first_size{15};\n  constexpr size_t second_size{55};\n  constexpr size_t third_size{emio::default_cache_size};\n\n  const std::string expected_str_part_1(first_size, 'x');\n  const std::string expected_str_part_2(second_size, 'y');\n  const std::string expected_str_part_3(third_size, 'z');\n  const std::string expected_str = expected_str_part_1 + expected_str_part_2 + expected_str_part_3;\n\n  const bool default_constructed = GENERATE(true, false);\n  const int checkpoint = GENERATE(range(0, 5));\n  INFO(\"Default constructed: \" << default_constructed);\n  INFO(\"Checkpoint: \" << checkpoint);\n\n  emio::memory_buffer<15> buf = default_constructed ? emio::memory_buffer<15>{} : emio::memory_buffer<15>{18};\n  CHECK(buf.capacity() == (default_constructed ? 15 : 18));\n  CHECK(buf.view().empty());\n\n  if (checkpoint == 0) {\n    check_gang_of_5(buf, true);\n    return;\n  }\n\n  emio::result<std::span<char>> area = buf.get_write_area_of(first_size);\n  REQUIRE(area);\n  CHECK(area->size() == first_size);\n  fill(area, 'x');\n  CHECK(buf.view().size() == first_size);\n  CHECK(buf.view() == expected_str_part_1);\n\n  if (checkpoint == 1) {\n    check_gang_of_5(buf, true);\n    return;\n  }\n\n  area = buf.get_write_area_of(second_size);\n  REQUIRE(area);\n  CHECK(area->size() == second_size);\n  fill(area, 'y');\n  CHECK(buf.view().size() == first_size + second_size);\n  CHECK(buf.view() == expected_str_part_1 + expected_str_part_2);\n\n  if (checkpoint == 2) {\n    check_gang_of_5(buf, true);\n    return;\n  }\n\n  area = buf.get_write_area_of(third_size);\n  REQUIRE(area);\n  CHECK(area->size() == third_size);\n  fill(area, 'z');\n  CHECK(buf.view() == buf.str());\n  CHECK(buf.view().size() == first_size + second_size + third_size);\n  CHECK(buf.view() == expected_str);\n\n  if (checkpoint == 3) {\n    check_gang_of_5(buf, true);\n    return;\n  }\n\n  const size_t curr_capacity = buf.capacity();\n  CHECK(curr_capacity >= buf.view().size());\n  buf.reset();\n  CHECK(buf.capacity() == curr_capacity);\n\n  area = buf.get_write_area_of(first_size);\n  REQUIRE(area);\n  CHECK(area->size() == first_size);\n\n  check_gang_of_5(buf, true);\n}\n\nTEST_CASE(\"memory_buffer regression bug 1\", \"[buffer]\") {\n  // Regression description:\n  // memory_buffer::request_write_area(...) passed an invalid too large write area to buffer::set_write_area(...).\n\n  // Test strategy:\n  // * Construct a memory_buffer and write 'a's into it for more than the default capacity of an std::string to\n  //   trigger a resize.\n  // * Write a 'b'. This memory location was a buffer overflow and not seen after another resize.\n  // * Write enough 'c's into it to trigger another resize. Otherwise, the issue would only be visible with memory\n  //   checks.\n  // Expected: Everything is correctly written.\n\n  const size_t default_capacity = std::string{}.capacity();\n  const size_t default_resize_capacity = [] {\n    std::string s;\n    s.resize(s.capacity() + 1U);\n    return s.capacity();\n  }();\n\n  emio::memory_buffer buf;\n  CHECK(buf.capacity() == emio::default_cache_size);\n\n  // Write a.\n  for (size_t i = 0; i < default_capacity + 1U; i++) {\n    buf.get_write_area_of(1).value()[0] = 'a';\n  }\n\n  // Write b.\n  buf.get_write_area_of(1).value()[0] = 'b';\n\n  // Write c.\n  const emio::result<std::span<char>> area2 = buf.get_write_area_of(default_resize_capacity);\n  fill(area2, 'c');\n\n  std::string expected_str(default_capacity + 1, 'a');\n  expected_str += 'b';\n  std::fill_n(std::back_inserter(expected_str), default_resize_capacity, 'c');\n\n  CHECK(buf.str() == expected_str);\n}\n\nTEST_CASE(\"memory_buffer at compile-time\", \"[buffer]\") {\n  // Test strategy:\n  // * Construct a ct_memory_buffer.\n  // * Write into the buffer at compile time.\n  // Expected: Everything is correctly written.\n\n  constexpr size_t first_size{7};\n  constexpr size_t second_size{5};\n  constexpr size_t third_size{8};\n\n  constexpr bool success = [] {\n    bool result = true;\n\n    emio::memory_buffer<1> buf{};\n    result &= buf.capacity() == 1;\n    result &= buf.view().empty();\n\n    emio::result<std::span<char>> area = buf.get_write_area_of(first_size);\n    result &= area->size() == first_size;\n    fill(area, 'x');\n    result &= buf.view().size() == first_size;\n    result &= buf.view() == \"xxxxxxx\";\n\n    area = buf.get_write_area_of(second_size);\n    result &= area->size() == second_size;\n    fill(area, 'y');\n    result &= buf.view().size() == first_size + second_size;\n    result &= buf.view() == \"xxxxxxxyyyyy\";\n\n    area = buf.get_write_area_of(third_size);\n    result &= area->size() == third_size;\n    fill(area, 'z');\n    result &= buf.view().size() == first_size + second_size + third_size;\n    result &= buf.view() == \"xxxxxxxyyyyyzzzzzzzz\";\n    result &= buf.capacity() >= buf.view().size();\n\n    const size_t curr_capacity = buf.capacity();\n    result &= curr_capacity >= buf.view().size();\n    buf.reset();\n    result &= buf.capacity() == curr_capacity;\n\n    area = buf.get_write_area_of(first_size);\n    result &= area.has_value();\n    result &= (area->size() == first_size);\n\n    emio::memory_buffer<1> buf2{buf};\n    emio::memory_buffer<1> buf3{std::move(buf2)};\n    emio::memory_buffer<1> buf4;\n    buf4 = buf3;\n    emio::memory_buffer<1> buf5;\n    buf5 = std::move(buf4);\n\n    result &= check_equality_of_buffer(buf, buf5, true);\n\n    return result;\n  }();\n  STATIC_CHECK(success);\n}\n\nTEST_CASE(\"span_buffer\", \"[buffer]\") {\n  // Test strategy:\n  // * Construct a span_buffer from an std::array.\n  // * Write into the buffer.\n  // Expected: Everything is directly written to the array.\n\n  constexpr size_t first_size{15};\n  constexpr size_t second_size{55};\n  constexpr size_t third_size{emio::default_cache_size};\n\n  const int checkpoint = GENERATE(range(0, 6));\n  INFO(\"Checkpoint: \" << checkpoint);\n\n  std::array<char, first_size + second_size + third_size> storage{};\n  emio::span_buffer buf{storage};\n  CHECK(buf.capacity() == storage.size());\n  CHECK(buf.view().empty());\n\n  if (checkpoint == 0) {\n    check_gang_of_5(buf, false);\n    return;\n  }\n\n  emio::result<std::span<char>> area = buf.get_write_area_of(first_size);\n  REQUIRE(area);\n  CHECK(area->size() == first_size);\n  CHECK(area->data() == storage.data());\n\n  CHECK(buf.view().size() == first_size);\n  CHECK(buf.view().data() == storage.data());\n\n  if (checkpoint == 1) {\n    check_gang_of_5(buf, false);\n    return;\n  }\n\n  area = buf.get_write_area_of(second_size);\n  REQUIRE(area);\n  CHECK(area->size() == second_size);\n  CHECK(area->data() == storage.data() + first_size);\n\n  CHECK(buf.view().size() == first_size + second_size);\n  CHECK(buf.view().data() == storage.data());\n\n  if (checkpoint == 2) {\n    check_gang_of_5(buf, false);\n    return;\n  }\n\n  area = buf.get_write_area_of(third_size);\n  REQUIRE(area);\n  CHECK(area->size() == third_size);\n  CHECK(area->data() == storage.data() + first_size + second_size);\n\n  CHECK(buf.view().size() == first_size + second_size + third_size);\n  CHECK(buf.view().data() == storage.data());\n  CHECK(buf.view() == buf.str());\n\n  if (checkpoint == 3) {\n    check_gang_of_5(buf, false);\n    return;\n  }\n\n  area = buf.get_write_area_of(1);\n  REQUIRE(!area);\n\n  area = buf.get_write_area_of(0);\n  REQUIRE(area);\n  CHECK(area->empty());\n\n  if (checkpoint == 4) {\n    check_gang_of_5(buf, false);\n    return;\n  }\n\n  CHECK(buf.capacity() == storage.size());\n  buf.reset();\n  CHECK(buf.capacity() == storage.size());\n  area = buf.get_write_area_of(first_size);\n  REQUIRE(area);\n  CHECK(area->size() == first_size);\n\n  check_gang_of_5(buf, false);\n}\n\nTEST_CASE(\"span_buffer check request_write_area\", \"[buffer]\") {\n  // Test strategy:\n  // * To explicit test if the span_buffer cannot provide a greater write area, we have to extend the class to access\n  //   the protected function.\n  // Expected: request_write_area always returns EOF.\n\n  class dummy_span_buffer : public emio::span_buffer {\n   public:\n    using emio::span_buffer::request_write_area;\n    using emio::span_buffer::span_buffer;\n  };\n\n  SECTION(\"default constructed\") {\n    dummy_span_buffer buf;\n    CHECK(buf.get_write_area_of_max(100) == emio::err::eof);\n    CHECK(buf.request_write_area(0, 0) == emio::err::eof);\n  }\n\n  SECTION(\"constructed with storage\") {\n    std::array<char, 50> storage;\n    dummy_span_buffer buf{storage};\n    CHECK(buf.get_write_area_of_max(100).value().size() == 50);\n    CHECK(buf.request_write_area(0, 0) == emio::err::eof);\n  }\n}\n\nTEST_CASE(\"buffer regression bug 1\", \"[buffer]\") {\n  // Regression description:\n  // buffer::get_write_area_of_max(...) tried to request more data from the concrete buffer implementation if the\n  // requested size is greater than available. For fixed size buffers (e.g. span buffer) this will always return EOF.\n  // Instead, the remaining bytes should be returned.\n\n  // Test strategy:\n  // * Construct a span_buffer over an std::array and request more bytes than available through get_write_area_of_max.\n  // Expected: A write area matching the std::array size is returned.\n\n  std::array<char, 50> arr;\n  emio::span_buffer buf{arr};\n  emio::result<std::span<char>> area = buf.get_write_area_of_max(60);\n  REQUIRE(area);\n  CHECK(area->size() == 50);\n\n  // No write area available anymore.\n  area = buf.get_write_area_of_max(60);\n  REQUIRE(!area);\n\n  // Except for zero bytes.\n  area = buf.get_write_area_of_max(0);\n  REQUIRE(area);\n  CHECK(area->empty());\n}\n\nTEST_CASE(\"static_buffer\", \"[buffer]\") {\n  // Test strategy:\n  // * Construct a static buffer.\n  // * Check max size of the buffer.\n  // Expected: The max size is correct.\n\n  const int checkpoint = GENERATE(range(0, 6));\n  INFO(\"Checkpoint: \" << checkpoint);\n\n  emio::static_buffer<542> buf;\n\n  if (checkpoint == 0) {\n    check_gang_of_5(buf, true);\n    return;\n  }\n\n  emio::result<std::span<char>> area = buf.get_write_area_of_max(500);\n  REQUIRE(area);\n  CHECK(area->size() == 500);\n  fill(area, 'q');\n\n  if (checkpoint == 1) {\n    check_gang_of_5(buf, true);\n    return;\n  }\n\n  area = buf.get_write_area_of_max(5);\n  REQUIRE(area);\n  CHECK(area->size() == 5);\n  fill(area, 'y');\n\n  if (checkpoint == 2) {\n    check_gang_of_5(buf, true);\n    return;\n  }\n\n  buf.reset();\n\n  if (checkpoint == 3) {\n    check_gang_of_5(buf, true);\n    return;\n  }\n\n  area = buf.get_write_area_of_max(600);\n  REQUIRE(area);\n  CHECK(area->size() == 542);\n  fill(area, 'l');\n\n  if (checkpoint == 4) {\n    check_gang_of_5(buf, true);\n    return;\n  }\n\n  buf.reset();\n\n  check_gang_of_5(buf, true);\n}\n\nTEST_CASE(\"counting_buffer\", \"[buffer]\") {\n  // Test strategy:\n  // * Construct a counting_buffer.\n  // * Write into the buffer.\n  // Expected: Every char written into the buffer is counted.\n\n  constexpr size_t first_size{15};\n  constexpr size_t second_size{55};\n  constexpr size_t third_size{emio::default_cache_size};\n\n  using emio::default_cache_size;\n\n  emio::detail::counting_buffer buf;\n  CHECK(buf.count() == 0);\n\n  emio::result<std::span<char>> area = buf.get_write_area_of(first_size);\n  REQUIRE(area);\n  REQUIRE(area->size() == first_size);\n\n  CHECK(buf.count() == first_size);\n\n  area = buf.get_write_area_of(second_size);\n  REQUIRE(area);\n  REQUIRE(area->size() == second_size);\n\n  CHECK(buf.count() == first_size + second_size);\n\n  area = buf.get_write_area_of(third_size);\n  REQUIRE(area);\n  REQUIRE(area->size() == third_size);\n\n  CHECK(buf.count() == (first_size + second_size + third_size));\n\n  area = buf.get_write_area_of(default_cache_size);\n  REQUIRE(area);\n  REQUIRE(area->size() == default_cache_size);\n\n  CHECK(buf.count() == (first_size + second_size + third_size + default_cache_size));\n\n  area = buf.get_write_area_of(default_cache_size + 1);\n  CHECK(!area);\n  CHECK(buf.count() == (first_size + second_size + 2 * third_size));\n}\n\nTEST_CASE(\"iterator_buffer<char ptr>\", \"[buffer]\") {\n  // Test strategy:\n  // * Construct an iterator_buffer from a char pointer.\n  // * Write into the buffer.\n  // Expected: Everything is directly written to the char pointer.\n\n  std::array<char, 5> s{};\n  emio::iterator_buffer it_buf{s.data()};\n\n  // First write.\n  emio::result<std::span<char>> area = it_buf.get_write_area_of(2);\n  REQUIRE(area);\n  REQUIRE(area->size() == 2);\n\n  fill(area, 'x');\n\n  CHECK((it_buf.out() == std::next(s.data(), 2)));\n\n  // Second write.\n  area = it_buf.get_write_area_of(3);\n  REQUIRE(area);\n  REQUIRE(area->size() == 3);\n\n  fill(area, 'y');\n\n  CHECK((it_buf.out() == std::next(s.data(), 5)));\n\n  CHECK(it_buf.flush());  // noop\n  CHECK((it_buf.out() == std::next(s.data(), 5)));\n  CHECK(std::string_view{s.data(), it_buf.out()} == \"xxyyy\");\n}\n\nTEST_CASE(\"iterator_buffer<iterator>\", \"[buffer]\") {\n  // Test strategy:\n  // * Construct an iterator_buffer from a std::string::iterator.\n  // * Write different data lengths into the buffer to test the internal buffer and flush mechanism.\n  // Expected: At the end (final flush/destruction), everything is written into the string.\n\n  const std::string expected_str_part_1(emio::default_cache_size, 'x');\n  const std::string expected_str_part_2(2, 'y');\n  const std::string expected_str_part_3(emio::default_cache_size, 'z');\n  const std::string expected_str_part_4(3, 'x');\n  const std::string expected_str_part_5(2, 'y');\n  const std::string expected_str_part_6(42, 'z');\n  const std::string expected_str = expected_str_part_1 + expected_str_part_2 + expected_str_part_3 +\n                                   expected_str_part_4 + expected_str_part_5 + expected_str_part_6;\n\n  std::string s(expected_str.size(), '\\0');\n\n  emio::iterator_buffer it_buf{s.begin()};\n\n  CHECK(it_buf.out() == s.begin());\n  CHECK(it_buf.get_write_area_of(std::numeric_limits<size_t>::max()) == emio::err::eof);\n  CHECK(it_buf.get_write_area_of(emio::default_cache_size + 1) == emio::err::eof);\n\n  emio::result<std::span<char>> area = it_buf.get_write_area_of(emio::default_cache_size);\n  REQUIRE(area);\n  REQUIRE(area->size() == emio::default_cache_size);\n  fill(area, 'x');\n\n  // No flush yet.\n  CHECK(s.starts_with('\\0'));\n\n  area = it_buf.get_write_area_of(2);\n  REQUIRE(area);\n  REQUIRE(area->size() == 2);\n  fill(area, 'y');\n\n  // 1. flush.\n  CHECK(s.starts_with(expected_str_part_1));\n\n  area = it_buf.get_write_area_of(emio::default_cache_size);\n  REQUIRE(area);\n  REQUIRE(area->size() == emio::default_cache_size);\n  fill(area, 'z');\n\n  // 2. flush.\n  CHECK(s.starts_with(expected_str_part_1 + expected_str_part_2));\n\n  area = it_buf.get_write_area_of(3);\n  REQUIRE(area);\n  REQUIRE(area->size() == 3);\n  fill(area, 'x');\n\n  // 3. flush.\n  CHECK(s.starts_with(expected_str_part_1 + expected_str_part_2 + expected_str_part_3));\n\n  area = it_buf.get_write_area_of(2);\n  REQUIRE(area);\n  REQUIRE(area->size() == 2);\n  fill(area, 'y');\n\n  // No flush.\n  CHECK(s.starts_with(expected_str_part_1 + expected_str_part_2 + expected_str_part_3));\n\n  area = it_buf.get_write_area_of(42);\n  REQUIRE(area);\n  REQUIRE(area->size() == 42);\n  fill(area, 'z');\n\n  // No flush.\n  CHECK(s.starts_with(expected_str_part_1 + expected_str_part_2 + expected_str_part_3));\n  CHECK(s != expected_str);\n\n  CHECK(it_buf.flush());\n  CHECK(s == expected_str);\n  CHECK(std::distance(it_buf.out(), s.end()) == 0);\n\n  CHECK(it_buf.flush());\n  CHECK(s == expected_str);\n  CHECK(std::distance(it_buf.out(), s.end()) == 0);\n}\n\nTEST_CASE(\"iterator_buffer<back_insert_iterator>\", \"[buffer]\") {\n  // Test strategy:\n  // * Construct an iterator_buffer from a char pointer.\n  // * Write into the buffer.\n  // Expected: Everything is directly written to the string.\n\n  const std::string expected_str_part_1(2, 'x');\n  const std::string expected_str_part_2(42, 'y');\n  const std::string expected_str_part_3(3, 'z');\n  const std::string expected_str = expected_str_part_1 + expected_str_part_2 + expected_str_part_3;\n\n  std::string s;\n  emio::iterator_buffer it_buf{std::back_inserter(s)};\n  STATIC_CHECK(std::is_same_v<decltype(it_buf.out()), decltype(std::back_inserter(s))>);\n\n  // First write.\n  emio::result<std::span<char>> area = it_buf.get_write_area_of(2);\n  REQUIRE(area);\n  REQUIRE(area->size() == 2);\n\n  fill(area, 'x');\n\n  CHECK(s.starts_with(expected_str_part_1));\n\n  // Second write.\n  area = it_buf.get_write_area_of(42);\n  REQUIRE(area);\n  REQUIRE(area->size() == 42);\n\n  fill(area, 'y');\n\n  CHECK(s.starts_with(expected_str_part_1 + expected_str_part_2));\n\n  // Third write.\n  area = it_buf.get_write_area_of(3);\n  REQUIRE(area);\n  REQUIRE(area->size() == 3);\n\n  fill(area, 'z');\n\n  CHECK(s.starts_with(expected_str));\n\n  // No flush - no final content.\n  CHECK(s != expected_str);\n\n  CHECK(it_buf.flush());\n  CHECK(s == expected_str);\n\n  CHECK(it_buf.flush());\n  CHECK(s == expected_str);\n\n  CHECK_NOTHROW(it_buf.out());\n}\n\nTEST_CASE(\"file_buffer\", \"[buffer]\") {\n  // Test strategy:\n  // * Construct a file_buffer with a temporary file stream.\n  // * Write into the buffer, flush (or not) and read out again.\n  // Expected: Everything is written to the file stream after flush.\n\n  // Open a temporary file.\n  std::FILE* tmpf = std::tmpfile();\n  REQUIRE(tmpf);\n\n  emio::file_buffer file_buf{tmpf};\n  std::array<char, 2 * emio::default_cache_size> read_out_buf{};\n\n  // Write area is limited.\n  CHECK(file_buf.get_write_area_of(emio::default_cache_size + 1) == emio::err::eof);\n\n  // Write into.\n  emio::result<std::span<char>> area = file_buf.get_write_area_of(2);\n  REQUIRE(area);\n  REQUIRE(area->size() == 2);\n  fill(area, 'y');\n\n  // Read out (without flush).\n  std::rewind(tmpf);\n  CHECK(std::fgets(read_out_buf.data(), read_out_buf.size(), tmpf) == nullptr);\n  CHECK(std::string_view{read_out_buf.data(), 4} == std::string_view{\"\\0\\0\\0\\0\", 4});\n\n  // Flush.\n  CHECK(file_buf.flush());\n\n  // Read out (after flush).\n  std::rewind(tmpf);\n  CHECK(std::fgets(read_out_buf.data(), read_out_buf.size(), tmpf));\n  CHECK(std::string_view{read_out_buf.data(), 4} == std::string_view{\"yy\\0\\0\", 4});\n\n  // Write into again.\n  area = file_buf.get_write_area_of(4);\n  REQUIRE(area);\n  REQUIRE(area->size() == 4);\n  fill(area, 'z');\n\n  // Read out (without flush).\n  std::rewind(tmpf);\n  CHECK(std::fgets(read_out_buf.data(), read_out_buf.size(), tmpf));\n  CHECK(std::string_view{read_out_buf.data(), 6} == std::string_view{\"yy\\0\\0\\0\\0\", 6});\n\n  // Flush.\n  CHECK(file_buf.flush());\n\n  std::rewind(tmpf);\n  CHECK(std::fgets(read_out_buf.data(), read_out_buf.size(), tmpf));\n  CHECK(std::string_view{read_out_buf.data(), 6} == \"yyzzzz\");\n\n  const std::string expected_long_str_part(emio::default_cache_size, 'x');\n\n  area = file_buf.get_write_area_of(emio::default_cache_size);\n  REQUIRE(area);\n  REQUIRE(area->size() == emio::default_cache_size);\n  fill(area, 'x');\n\n  // Internal flush should have happened.\n  area = file_buf.get_write_area_of(2);\n  REQUIRE(area);\n  REQUIRE(area->size() == 2);\n  fill(area, 'z');\n\n  std::rewind(tmpf);\n  CHECK(std::fgets(read_out_buf.data(), read_out_buf.size(), tmpf));\n  CHECK(std::string_view{read_out_buf.data(), 6 + emio::default_cache_size} == \"yyzzzz\" + expected_long_str_part);\n}\n\nTEST_CASE(\"file_buffer reset\", \"[buffer]\") {\n  std::FILE* tmpf = std::tmpfile();\n  REQUIRE(tmpf);\n\n  emio::file_buffer file_buf{tmpf};\n  std::array<char, 2 * emio::default_cache_size> read_out_buf{};\n\n  // Write into.\n  emio::result<std::span<char>> area = file_buf.get_write_area_of(4);\n  REQUIRE(area);\n  REQUIRE(area->size() == 4);\n  fill(area, 'x');\n\n  // Nothing should be written.\n  file_buf.reset();\n  CHECK(file_buf.flush());\n\n  CHECK(std::fgets(read_out_buf.data(), read_out_buf.size(), tmpf) == nullptr);\n\n  area = file_buf.get_write_area_of(4);\n  REQUIRE(area);\n  REQUIRE(area->size() == 4);\n  fill(area, 'y');\n\n  // Nothing should be written.\n  file_buf.reset();\n\n  area = file_buf.get_write_area_of(4);\n  REQUIRE(area);\n  REQUIRE(area->size() == 4);\n  fill(area, 'z');\n\n  // Flush and then reset.\n  CHECK(file_buf.flush());\n  file_buf.reset();\n\n  CHECK(std::fgets(read_out_buf.data(), read_out_buf.size(), tmpf));\n  CHECK(std::string_view{read_out_buf.data(), 4} == \"zzzz\");\n}\n\nTEST_CASE(\"truncating_buffer\", \"[buffer]\") {\n  emio::static_buffer<64> primary_buf;\n\n  SECTION(\"request less than limit (1)\") {\n    const bool explicit_flush = GENERATE(false, true);\n    const std::string expected_string = std::string(40, 'a') + std::string(8, 'b');\n    emio::truncating_buffer buf{primary_buf, 48};\n\n    emio::result<std::span<char>> area = buf.get_write_area_of(40);\n    REQUIRE(area);\n    fill(area, 'a');\n    CHECK(buf.count() == 40);\n\n    // not flushed\n    CHECK(primary_buf.view().size() == 0);\n    if (explicit_flush) {\n      // flushed\n      CHECK(buf.flush());\n      CHECK(primary_buf.view().size() == 40);\n    }\n\n    SECTION(\"request less than limit (2)\") {\n      area = buf.get_write_area_of(8);\n      REQUIRE(area);\n      fill(area, 'b');\n      CHECK(buf.count() == 48);\n\n      if (explicit_flush) {\n        // not flushed\n        CHECK(primary_buf.view().size() == 40);\n        // flushed\n        CHECK(buf.flush());\n        CHECK(primary_buf.view().size() == 48);\n      } else {\n        CHECK(primary_buf.view().size() == 0);\n      }\n\n      SECTION(\"request more than limit (3)\") {\n        area = buf.get_write_area_of(emio::default_cache_size);\n        REQUIRE(area);\n        fill(area, 'c');\n        CHECK(buf.count() == 48 + emio::default_cache_size);\n\n        // not flushed\n        CHECK(primary_buf.view().size() == 48);\n        // flushed\n        CHECK(buf.flush());\n        CHECK(primary_buf.view().size() == 48);\n\n        CHECK(primary_buf.view() == expected_string);\n      }\n    }\n    SECTION(\"request more than limit (2)\") {\n      area = buf.get_write_area_of(emio::default_cache_size);\n      REQUIRE(area);\n      fill(area, 'b');\n      CHECK(buf.count() == 40 + emio::default_cache_size);\n\n      // not flushed\n      CHECK(primary_buf.view().size() == 40);\n      // flushed\n      CHECK(buf.flush());\n      CHECK(primary_buf.view().size() == 48);\n\n      CHECK(primary_buf.view() == expected_string);\n    }\n  }\n  SECTION(\"request more than limit (1)\") {\n    const std::string expected_string = std::string(48, 'a');\n    emio::truncating_buffer buf{primary_buf, 48};\n\n    emio::result<std::span<char>> area = buf.get_write_area_of(emio::default_cache_size);\n    REQUIRE(area);\n    fill(area, 'a');\n    CHECK(buf.count() == emio::default_cache_size);\n\n    // not flushed\n    CHECK(primary_buf.view().size() == 0);\n    const bool explicit_flush = GENERATE(false, true);\n    if (explicit_flush) {\n      // flushed\n      CHECK(buf.flush());\n      CHECK(primary_buf.view().size() == 48);\n    }\n\n    SECTION(\"request more than limit (2)\") {\n      CHECK(buf.get_write_area_of(emio::default_cache_size));\n      CHECK(buf.count() == 2 * emio::default_cache_size);\n\n      // not flushed\n      CHECK(primary_buf.view().size() == 48);\n      // flushed\n      CHECK(buf.flush());\n      CHECK(primary_buf.view().size() == 48);\n\n      CHECK(primary_buf.view() == expected_string);\n    }\n  }\n  SECTION(\"request more than primary\") {\n    emio::truncating_buffer buf{primary_buf, 86};\n\n    SECTION(\"request less than limit (1)\") {\n      const std::string expected_string = std::string(60, 'a') + std::string(4, 'b');\n\n      emio::result<std::span<char>> area = buf.get_write_area_of(60);\n      REQUIRE(area);\n      fill(area, 'a');\n      CHECK(buf.count() == 60);\n\n      // not flushed\n      CHECK(primary_buf.view().size() == 0);\n      const bool explicit_flush = GENERATE(false, true);\n      if (explicit_flush) {\n        // flushed\n        CHECK(buf.flush());\n        CHECK(primary_buf.view().size() == 60);\n      }\n\n      SECTION(\"request less than limit twice (2)\") {\n        area = buf.get_write_area_of(8);\n        REQUIRE(area);\n        fill(area, 'b');\n        CHECK(buf.count() == 68);\n\n        // Requesting more will fail because primary buffer is too small.\n        CHECK_FALSE(buf.get_write_area_of(emio::default_cache_size));\n\n        CHECK(primary_buf.view().size() == 64);\n        CHECK(primary_buf.view() == expected_string);\n      }\n    }\n    SECTION(\"request more than limit (1)\") {\n      const std::string expected_string = std::string(64, 'a');\n\n      emio::result<std::span<char>> area = buf.get_write_area_of(90);\n      REQUIRE(area);\n      fill(area, 'a');\n      CHECK(buf.count() == 90);\n\n      // not flushed\n      CHECK(primary_buf.view().size() == 0);\n      // flush not possible because primary buffer is too small\n      CHECK_FALSE(buf.flush());\n\n      CHECK(primary_buf.view().size() == 64);\n      CHECK(primary_buf.view() == expected_string);\n    }\n  }\n  SECTION(\"request more than the cache size\") {\n    const std::string expected_string = std::string(64, 'a');\n    emio::truncating_buffer buf{primary_buf, 64};\n\n    emio::result<std::span<char>> area = buf.get_write_area_of_max(emio::default_cache_size + 10);\n    REQUIRE(area);\n    CHECK(area->size() == emio::default_cache_size);\n    fill(area, 'a');\n\n    // not flushed\n    CHECK(primary_buf.view().size() == 0);\n    // flush not possible because primary buffer is too small\n    CHECK(buf.flush());\n\n    CHECK(primary_buf.view().size() == 64);\n    CHECK(primary_buf.view() == expected_string);\n  }\n}\n"
  },
  {
    "path": "test/unit_test/test_dynamic_format_spec.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n#include <emio/ranges.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namespace std::string_view_literals;\n\nnamespace {\n\nclass no_copy_or_move {\n public:\n  no_copy_or_move() = default;\n  no_copy_or_move(const no_copy_or_move&) = delete;\n  no_copy_or_move(no_copy_or_move&&) = delete;\n  no_copy_or_move& operator=(const no_copy_or_move&) = delete;\n  no_copy_or_move& operator=(no_copy_or_move&&) = delete;\n};\n\nconstexpr auto format_as(const no_copy_or_move& /*unused*/) {\n  return \"nothing\";\n}\n\n}  // namespace\n\nTEST_CASE(\"dynamic format spec\") {\n  SECTION(\"default constructed\") {\n    // Spec doesn't overwrite anything.\n    emio::format_spec spec{};\n\n    CHECK(emio::format(\"{}\", spec.with(5)) == \"5\");\n    CHECK(emio::format(\"{:4}\", spec.with(5)) == \"   5\");\n    CHECK(emio::format(\"{:f}\", spec.with(5.1)) == \"5.100000\");\n    CHECK(emio::format(\"{:.2f}\", spec.with(5.1)) == \"5.10\");\n  }\n\n  SECTION(\"custom width and precision\") {\n    // Spec always overwrite.\n    emio::format_spec spec{.width = 3, .precision = 3};\n\n    CHECK(emio::format(\"{}\", spec.with(5)) == \"  5\");\n    CHECK(emio::format(\"{:4}\", spec.with(5)) == \"  5\");\n    CHECK(emio::format(\"{:f}\", spec.with(5.1)) == \"5.100\");\n    CHECK(emio::format(\"{:.0f}\", spec.with(5.1)) == \"5.100\");\n  }\n\n  SECTION(\"with(...) doesn't copy or move\") {\n    // Object is not copied or moved because a reference is held.\n    emio::format_spec spec{};\n\n    CHECK(emio::format(\"{}\", spec.with(no_copy_or_move{})) == \"nothing\");\n\n    no_copy_or_move obj{};\n    CHECK(emio::format(\"{}\", spec.with(obj)) == \"nothing\");\n    CHECK(emio::format(\"{}\", spec.with(std::move(obj))) == \"nothing\");\n  }\n\n  SECTION(\"compile-time\") {\n    constexpr bool success = [] {\n      emio::static_buffer<10> buf{};\n\n      emio::format_spec spec{.width = 4};\n      static_cast<void>(emio::format_to(buf, \"{:^}\", spec.with(42)).value());\n      return buf.view() == \" 42 \";\n    }();\n    STATIC_CHECK(success);\n  }\n\n  SECTION(\"works with contiguous ranges\") {\n    emio::format_spec spec{.precision = 2};\n\n    std::array<double, 3> values{2.099, 3.245, 4.5};\n    CHECK(emio::format(\"{::f}\", spec.with(values)) == \"[2.10, 3.25, 4.50]\");\n  }\n\n  SECTION(\"with runtime format string\") {\n    emio::format_spec spec{.precision = 1};\n\n    CHECK(emio::format(emio::runtime(\"{:f}\"), spec.with(3.14)).value() == \"3.1\");\n  }\n}\n"
  },
  {
    "path": "test/unit_test/test_format.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <climits>\n#include <cmath>\n\nusing namespace std::string_view_literals;\n\n// Test cases from fmt/test/format-test.cc - 9.1.0\n\nnamespace {\n\ntemplate <typename... Args>\nbool validate_format_string(std::string_view str) {\n  return emio::detail::format::format_trait::validate_string<Args...>(str);\n}\n\n}  // namespace\n\nTEST_CASE(\"escape\") {\n  CHECK(emio::format(\"{{\") == \"{\");\n  CHECK(emio::format(\"before {{\") == \"before {\");\n  CHECK(emio::format(\"{{ after\") == \"{ after\");\n  CHECK(emio::format(\"before {{ after\") == \"before { after\");\n\n  CHECK(emio::format(\"}}\") == \"}\");\n  CHECK(emio::format(\"before }}\") == \"before }\");\n  CHECK(emio::format(\"}} after\") == \"} after\");\n  CHECK(emio::format(\"before }} after\") == \"before } after\");\n\n  CHECK(emio::format(\"{{}}\") == \"{}\");\n  CHECK(emio::format(\"{{{0}}}\", 42) == \"{42}\");\n}\n\nTEST_CASE(\"unmatched_braces\") {\n  CHECK(emio::format(emio::runtime(\"{\")) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"}\")) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0{}\")) == emio::err::invalid_format);\n}\n\nTEST_CASE(\"no_args\") {\n  CHECK(emio::format(\"test\") == \"test\");\n}\n\nTEST_CASE(\"args_in_different_positions\") {\n  CHECK(emio::format(\"{0}\", 42) == \"42\");\n  CHECK(emio::format(\"before {0}\", 42) == \"before 42\");\n  CHECK(emio::format(\"{0} after\", 42) == \"42 after\");\n  CHECK(emio::format(\"before {0} after\", 42) == \"before 42 after\");\n  CHECK(emio::format(\"{0} = {1}\", \"answer\", 42) == \"answer = 42\");\n  CHECK(emio::format(\"{1} is the {0}\", \"answer\", 42) == \"42 is the answer\");\n  CHECK(emio::format(\"{0}{1}{0}\", \"abra\", \"cad\") == \"abracadabra\");\n}\n\nTEST_CASE(\"auto_arg_index\") {\n  CHECK(emio::format(\"{}{}{}\", 'a', 'b', 'c') == \"abc\");\n  CHECK(emio::format(emio::runtime(\"{0}{}\"), 'a', 'b') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{}{0}\"), 'a', 'b') == emio::err::invalid_format);\n  CHECK(emio::format(\"{:.2}\", 1.2345) == \"1.2\");\n  CHECK(emio::format(emio::runtime(\"{0}:.{}\"), 1.2345, 2) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{:.{0}}\"), 1.2345, 2) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{}\")) == emio::err::invalid_format);\n}\n\nTEST_CASE(\"empty_specs\") {\n  CHECK(emio::format(\"{0:}\", 42) == \"42\");\n}\n\nTEST_CASE(\"left align\") {\n  CHECK(emio::format(\"{0:<}\", 42) == \"42\");\n  CHECK(emio::format(\"{0:<4}\", 42) == \"42  \");\n  CHECK(emio::format(\"{0:<4o}\", 042) == \"42  \");\n  CHECK(emio::format(\"{0:<4x}\", 0x42) == \"42  \");\n  CHECK(emio::format(\"{0:<5}\", -42) == \"-42  \");\n  CHECK(emio::format(\"{0:<5}\", 42u) == \"42   \");\n  CHECK(emio::format(\"{0:<5}\", -42l) == \"-42  \");\n  CHECK(emio::format(\"{0:<5}\", 42ul) == \"42   \");\n  CHECK(emio::format(\"{0:<5}\", -42ll) == \"-42  \");\n  CHECK(emio::format(\"{0:<5}\", 42ull) == \"42   \");\n  CHECK(emio::format(\"{0:<5}\", -42.0) == (\"-42  \"));\n  CHECK(emio::format(\"{0:<5}\", 'c') == \"c    \");\n  CHECK(emio::format(\"{0:<5}\", \"abc\") == \"abc  \");\n  CHECK(emio::format(\"{0:<8}\", reinterpret_cast<void*>(0xface)) == \"0xface  \");\n}\n\nTEST_CASE(\"right align\") {\n  CHECK(emio::format(\"{0:>}\", 42) == \"42\");\n  CHECK(emio::format(\"{0:>4}\", 42) == \"  42\");\n  CHECK(emio::format(\"{0:>4o}\", 042) == \"  42\");\n  CHECK(emio::format(\"{0:>4x}\", 0x42) == \"  42\");\n  CHECK(emio::format(\"{0:>5}\", -42) == \"  -42\");\n  CHECK(emio::format(\"{0:>5}\", 42u) == \"   42\");\n  CHECK(emio::format(\"{0:>5}\", -42l) == \"  -42\");\n  CHECK(emio::format(\"{0:>5}\", 42ul) == \"   42\");\n  CHECK(emio::format(\"{0:>5}\", -42ll) == \"  -42\");\n  CHECK(emio::format(\"{0:>5}\", 42ull) == \"   42\");\n  CHECK(emio::format(\"{0:>5}\", -42.0) == \"  -42\");\n  CHECK(emio::format(\"{0:>5}\", 'c') == \"    c\");\n  CHECK(emio::format(\"{0:>5}\", \"abc\") == \"  abc\");\n  CHECK(emio::format(\"{0:>8}\", reinterpret_cast<void*>(0xface)) == \"  0xface\");\n}\n\nTEST_CASE(\"center align\") {\n  CHECK(emio::format(\"{0:^}\", 42) == \"42\");\n  CHECK(emio::format(\"{0:^5}\", 42) == \" 42  \");\n  CHECK(emio::format(\"{0:^5o}\", 042) == \" 42  \");\n  CHECK(emio::format(\"{0:^5x}\", 0x42) == \" 42  \");\n  CHECK(emio::format(\"{0:^5}\", -42) == \" -42 \");\n  CHECK(emio::format(\"{0:^5}\", 42u) == \" 42  \");\n  CHECK(emio::format(\"{0:^5}\", -42l) == \" -42 \");\n  CHECK(emio::format(\"{0:^5}\", 42ul) == \" 42  \");\n  CHECK(emio::format(\"{0:^5}\", -42ll) == \" -42 \");\n  CHECK(emio::format(\"{0:^5}\", 42ull) == \" 42  \");\n  CHECK(emio::format(\"{0:^5}\", -42.0) == \" -42 \");\n  CHECK(emio::format(\"{0:^5}\", 'c') == \"  c  \");\n  CHECK(emio::format(\"{0:^6}\", \"abc\") == \" abc  \");\n  CHECK(emio::format(\"{0:^8}\", reinterpret_cast<void*>(0xface)) == \" 0xface \");\n}\n\nTEST_CASE(\"fill\") {\n  CHECK(emio::format(\"{0:*>4}\", 42) == \"**42\");\n  CHECK(emio::format(\"{0:*>5}\", -42) == \"**-42\");\n  CHECK(emio::format(\"{0:*>5}\", 42u) == \"***42\");\n  CHECK(emio::format(\"{0:*>5}\", -42l) == \"**-42\");\n  CHECK(emio::format(\"{0:*>5}\", 42ul) == \"***42\");\n  CHECK(emio::format(\"{0:*>5}\", -42ll) == \"**-42\");\n  CHECK(emio::format(\"{0:*>5}\", 42ull) == \"***42\");\n  CHECK(emio::format(\"{0:*>5}\", -42.0) == \"**-42\");\n  CHECK(emio::format(\"{0:*<5}\", 'c') == \"c****\");\n  CHECK(emio::format(\"{0:*<5}\", \"abc\") == \"abc**\");\n  CHECK(emio::format(\"{0:*>8}\", reinterpret_cast<void*>(0xface)) == \"**0xface\");\n  CHECK(emio::format(\"{:}=\", \"foo\") == \"foo=\");\n  CHECK(emio::format(emio::runtime(std::string_view(\"{:\\0>4}\", 6)), '*') == std::string(\"\\0\\0\\0*\", 4));\n  //  CHECK(emio::format(\"{0:ж>4}\", 42) == \"жж42\");\n}\n\nTEST_CASE(\"plus sign\") {\n  CHECK(emio::format(\"{0:+}\", 42) == \"+42\");\n  CHECK(emio::format(\"{0:+}\", -42) == \"-42\");\n  CHECK(emio::format(\"{0:+}\", 42) == \"+42\");\n  CHECK(emio::format(emio::runtime(\"{0:+}\"), 42u) == emio::err::invalid_format);\n  CHECK(emio::format(\"{0:+}\", 42l) == \"+42\");\n  CHECK(emio::format(emio::runtime(\"{0:+}\"), 42ul) == emio::err::invalid_format);\n  CHECK(emio::format(\"{0:+}\", 42ll) == \"+42\");\n  // #if FMT_USE_INT128\n  //  CHECK(emio::format(\"{0:+}\", __int128_t(42)) == \"+42\");\n  // #endif\n  CHECK(emio::format(emio::runtime(\"{0:+}\"), 42ull) == emio::err::invalid_format);\n  CHECK(emio::format(\"{0:+}\", 42.0) == \"+42\");\n  CHECK(emio::format(emio::runtime(\"{0:+\"), 'c') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:+}\"), 'c') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:+}\"), \"abc\") == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:+}\"), reinterpret_cast<void*>(0x42)) == emio::err::invalid_format);\n}\n\nTEST_CASE(\"minus sign\") {\n  CHECK(emio::format(\"{0:-}\", 42) == \"42\");\n  CHECK(emio::format(\"{0:-}\", -42) == \"-42\");\n  CHECK(emio::format(\"{0:-}\", 42) == \"42\");\n  CHECK(emio::format(emio::runtime(\"{0:-}\"), 42u) == emio::err::invalid_format);\n  CHECK(emio::format(\"{0:-}\", 42l) == \"42\");\n  CHECK(emio::format(\"{0:-}\", 42ll) == \"42\");\n  CHECK(emio::format(\"{0:-}\", 42.0) == \"42\");\n  CHECK(emio::format(emio::runtime(\"{0:-}\"), 'c') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:-}\"), \"abc\") == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:-}\"), reinterpret_cast<void*>(0x42)) == emio::err::invalid_format);\n}\n\nTEST_CASE(\"space sign\") {\n  CHECK(emio::format(\"{0: }\", 42) == \" 42\");\n  CHECK(emio::format(\"{0: }\", -42) == \"-42\");\n  CHECK(emio::format(\"{0: }\", 42) == \" 42\");\n  CHECK(emio::format(emio::runtime(\"{0: }\"), 42u) == emio::err::invalid_format);\n  CHECK(emio::format(\"{0: }\", 42l) == \" 42\");\n  CHECK(emio::format(emio::runtime(\"{0: }\"), 42ul) == emio::err::invalid_format);\n  CHECK(emio::format(\"{0: }\", 42ll) == \" 42\");\n  CHECK(emio::format(emio::runtime(\"{0: }\"), 42ull) == emio::err::invalid_format);\n  CHECK(emio::format(\"{0: }\", 42.0) == \" 42\");\n  CHECK(emio::format(emio::runtime(\"{0: \"), 'c') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0: }\"), 'c') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0: }\"), \"abc\") == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0: }\"), reinterpret_cast<void*>(0x42)) == emio::err::invalid_format);\n}\n\nTEST_CASE(\"hash flag\") {\n  CHECK(emio::format(\"{0:#}\", 42) == \"42\");\n  CHECK(emio::format(\"{0:#}\", -42) == \"-42\");\n  CHECK(emio::format(\"{0:#b}\", 42) == \"0b101010\");\n  CHECK(emio::format(\"{0:#B}\", 42) == \"0B101010\");\n  CHECK(emio::format(\"{0:#b}\", -42) == \"-0b101010\");\n  CHECK(emio::format(\"{0:#x}\", 0x42) == \"0x42\");\n  CHECK(emio::format(\"{0:#X}\", 0x42) == \"0X42\");\n  CHECK(emio::format(\"{0:#x}\", -0x42) == \"-0x42\");\n  CHECK(emio::format(\"{0:#o}\", 0) == \"0\");\n  CHECK(emio::format(\"{0:#o}\", 042) == \"042\");\n  CHECK(emio::format(\"{0:#o}\", -042) == \"-042\");\n  CHECK(emio::format(\"{0:#}\", 42u) == \"42\");\n  CHECK(emio::format(\"{0:#x}\", 0x42u) == \"0x42\");\n  CHECK(emio::format(\"{0:#o}\", 042u) == \"042\");\n\n  CHECK(emio::format(\"{0:#}\", -42l) == \"-42\");\n  CHECK(emio::format(\"{0:#x}\", 0x42l) == \"0x42\");\n  CHECK(emio::format(\"{0:#x}\", -0x42l) == \"-0x42\");\n  CHECK(emio::format(\"{0:#o}\", 042l) == \"042\");\n  CHECK(emio::format(\"{0:#o}\", -042l) == \"-042\");\n  CHECK(emio::format(\"{0:#}\", 42ul) == \"42\");\n  CHECK(emio::format(\"{0:#x}\", 0x42ul) == \"0x42\");\n  CHECK(emio::format(\"{0:#o}\", 042ul) == \"042\");\n\n  CHECK(emio::format(\"{0:#}\", -42ll) == \"-42\");\n  CHECK(emio::format(\"{0:#x}\", 0x42ll) == \"0x42\");\n  CHECK(emio::format(\"{0:#x}\", -0x42ll) == \"-0x42\");\n  CHECK(emio::format(\"{0:#o}\", 042ll) == \"042\");\n  CHECK(emio::format(\"{0:#o}\", -042ll) == \"-042\");\n  CHECK(emio::format(\"{0:#}\", 42ull) == \"42\");\n  CHECK(emio::format(\"{0:#x}\", 0x42ull) == \"0x42\");\n  CHECK(emio::format(\"{0:#o}\", 042ull) == \"042\");\n\n  CHECK(emio::format(\"{0:#}\", -42.0) == \"-42.\");\n  CHECK(emio::format(\"{:#.0e}\", 42.0) == \"4.e+01\");\n  CHECK(emio::format(\"{:#.0f}\", 0.01) == \"0.\");\n  CHECK(emio::format(\"{:#.2g}\", 0.5) == \"0.50\");\n  CHECK(emio::format(\"{:#.0f}\", 0.5) == \"0.\");\n  CHECK(emio::format(emio::runtime(\"{0:#\"), 'c') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:#}\"), 'c') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:#}\"), \"abc\") == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:#}\"), reinterpret_cast<void*>(0x42)) == emio::err::invalid_format);\n}\n\nTEST_CASE(\"zero flag\") {\n  CHECK(emio::format(emio::runtime(\"{0:0}\")) == emio::err::invalid_format);  // Zero without width doesn't make sense.\n  CHECK(emio::format(\"{0:05}\", -42) == \"-0042\");\n  CHECK(emio::format(\"{0:05}\", 42u) == \"00042\");\n  CHECK(emio::format(\"{0:05}\", -42l) == \"-0042\");\n  CHECK(emio::format(\"{0:05}\", 42ul) == \"00042\");\n  CHECK(emio::format(\"{0:05}\", -42ll) == \"-0042\");\n  CHECK(emio::format(\"{0:05}\", 42ull) == \"00042\");\n  CHECK(emio::format(\"{0:07}\", -42.0) == \"-000042\");\n  CHECK(emio::format(emio::runtime(\"{0:0\"), 'c') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:05}\"), 'c') == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:05}\"), \"abc\") == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:05}\"), reinterpret_cast<void*>(0x42)) == emio::err::invalid_format);\n}\n\nTEST_CASE(\"zero_flag_and_align\") {\n  // If the 0 character and an align option both appear, the 0 character is ignored.\n  CHECK(emio::format(\"{0:<05}\", 42) == \"42   \");\n  CHECK(emio::format(\"{0:<05}\", -42) == \"-42  \");\n  CHECK(emio::format(\"{0:^05}\", 42) == \" 42  \");\n  CHECK(emio::format(\"{0:^05}\", -42) == \" -42 \");\n  CHECK(emio::format(\"{0:>05}\", 42) == \"   42\");\n  CHECK(emio::format(\"{0:>05}\", -42) == \"  -42\");\n  CHECK(emio::format(\"{0:>05}\", -42) == \"  -42\");\n}\n\nTEST_CASE(\"zero_flag_sign_and_prefix\") {\n  CHECK(emio::format(\"{0:#8x}\", 42) == \"    0x2a\");\n  CHECK(emio::format(\"{0:+#8x}\", 42) == \"   +0x2a\");\n  CHECK(emio::format(\"{0:#08x}\", 42) == \"0x00002a\");\n  CHECK(emio::format(\"{0:+#08x}\", 42) == \"+0x0002a\");\n  CHECK(emio::format(\"{0:#8x}\", -42) == \"   -0x2a\");\n  CHECK(emio::format(\"{0:#08x}\", -42) == \"-0x0002a\");\n}\n\nTEST_CASE(\"width\") {\n  /*char format_str[buffer_size];\n  safe_sprintf(format_str, \"{0:%u\", UINT_MAX);\n  increment(format_str + 3);\n  EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,\n                   \"number is too big\");\n  size_t size = std::strlen(format_str);\n  format_str[size] = '}';\n  format_str[size + 1] = 0;\n  EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,\n                   \"number is too big\");\n\n  safe_sprintf(format_str, \"{0:%u\", INT_MAX + 1u);\n  EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,\n                   \"number is too big\");\n  safe_sprintf(format_str, \"{0:%u}\", INT_MAX + 1u);\n  EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,\n                   \"number is too big\");*/\n  CHECK(emio::format(\"{0:4}\", -42) == \" -42\");\n  CHECK(emio::format(\"{0:5}\", 42u) == \"   42\");\n  CHECK(emio::format(\"{0:6}\", -42l) == \"   -42\");\n  CHECK(emio::format(\"{0:7}\", 42ul) == \"     42\");\n  CHECK(emio::format(\"{0:6}\", -42ll) == \"   -42\");\n  CHECK(emio::format(\"{0:7}\", 42ull) == \"     42\");\n  CHECK(emio::format(\"{0:8}\", -1.23) == \"   -1.23\");\n  CHECK(emio::format(\"{0:10}\", reinterpret_cast<void*>(0xcafe)) == \"    0xcafe\");\n  CHECK(emio::format(\"{0:11}\", 'x') == \"x          \");\n  CHECK(emio::format(\"{0:12}\", \"str\") == \"str         \");\n  //  EXPECT_EQ(fmt::format(\"{:*^6}\", \"🤡\"), \"**🤡**\");\n  //  EXPECT_EQ(fmt::format(\"{:*^8}\", \"你好\"), \"**你好**\");\n  CHECK(emio::format(\"{:#6}\", 42.) == \"   42.\");\n  CHECK(emio::format(\"{:6c}\", static_cast<int>('x')) == \"x     \");\n  CHECK(emio::format(\"{:06.0f}\", 0.00884311) == \"000000\");\n  CHECK(emio::format(\"{:>06.0f}\", 0.00884311) == \"     0\");\n  CHECK(emio::format(\"{:5?}\", \"\\n\") == \"\\\"\\\\n\\\" \");\n}\n\nTEST_CASE(\"precision\") {\n  //  char format_str[buffer_size];\n  ////  safe_sprintf(format_str, \"{0:.%u\", UINT_MAX);\n  ////  increment(format_str + 4);\n  ////  EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0.0), format_error,\n  ////                   \"number is too big\");\n  ////  size_t size = std::strlen(format_str);\n  ////  format_str[size] = '}';\n  ////  format_str[size + 1] = 0;\n  ////  EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0.0), format_error,\n  ////                   \"number is too big\");\n  ////\n  ////  safe_sprintf(format_str, \"{0:.%u\", INT_MAX + 1u);\n  ////  EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0.0), format_error,\n  ////                   \"number is too big\");\n  ////  safe_sprintf(format_str, \"{0:.%u}\", INT_MAX + 1u);\n  ////  EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0.0), format_error,\n  ////                   \"number is too big\");\n\n  CHECK(emio::format(emio::runtime(\"{0:.\"), 0.0) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.}\"), 0.0) == emio::err::invalid_format);\n\n  CHECK(emio::format(emio::runtime(\"{0:.2\"), 0) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2}\"), 42) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2f}\"), 42) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2}\"), 42u) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2f}\"), 42u) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2}\"), 42l) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2f}\"), 42l) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2}\"), 42ul) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2f}\"), 42ul) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2}\"), 42ll) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2f}\"), 42ll) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2}\"), 42ull) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2f}\"), 42ull) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:3.0}\"), 'x') == emio::err::invalid_format);\n  CHECK(emio::format(\"{0:.2}\", 1.2345) == \"1.2\");\n  CHECK(emio::format(\"{:.2}\", 1.234e56) == \"1.2e+56\");\n  CHECK(emio::format(\"{0:.3}\", 1.1) == \"1.1\");\n  CHECK(emio::format(\"{:.0e}\", 1.0) == \"1e+00\");\n  CHECK(emio::format(\"{:9.1e}\", 0.0) == \"  0.0e+00\");\n  CHECK(emio::format(\"{:.494}\", 4.9406564584124654E-324) ==\n        \"4.9406564584124654417656879286822137236505980261432476442558568250067550\"\n        \"727020875186529983636163599237979656469544571773092665671035593979639877\"\n        \"479601078187812630071319031140452784581716784898210368871863605699873072\"\n        \"305000638740915356498438731247339727316961514003171538539807412623856559\"\n        \"117102665855668676818703956031062493194527159149245532930545654440112748\"\n        \"012970999954193198940908041656332452475714786901472678015935523861155013\"\n        \"480352649347201937902681071074917033322268447533357208324319361e-324\");\n  CHECK(emio::format(\"{:.1074f}\", 1.1125369292536e-308) ==\n        \"0.0000000000000000000000000000000000000000000000000000000000000000000000\"\n        \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n        \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n        \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n        \"000000000000000000000111253692925360019747947051741965785554081512200979\"\n        \"355021686109411883779182127659725163430929750364498219730822952552570601\"\n        \"152163505899912777129583674906301179059298598412303893909188340988729019\"\n        \"014361467448914817838555156840459458527907308695109202499990850735085304\"\n        \"478476991912072201449236975063640913461919914396877093174125167509869762\"\n        \"482369631100360266123742648159508919592746619553246586039571522788247697\"\n        \"156360766271842991667238355464496455107749716934387136380536472531224398\"\n        \"559833794807213172371254492216255558078524900147957309382830827524104234\"\n        \"530961756787819847850302379672357738807808384667004752163416921762619527\"\n        \"462847642037420991432005657440259928195996762610375541867198059294212446\"\n        \"81962777939941034720757232455434770912461317493580281734466552734375\");\n\n  std::string outputs[] = {\n      \"-0X1.41FE3FFE71C9E000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000P+127\",\n      \"-0XA.0FF1FFF38E4F0000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000000000000000000000000\"\n      \"000000000000000000000000000000000000000000000000000P+124\"};\n  //  EXPECT_THAT(outputs,\n  //              testing::Contains(fmt::format(\"{:.838A}\", -2.14001164E+38)));\n\n  //  if (std::numeric_limits<long double>::digits == 64) {\n  //    auto ld = (std::numeric_limits<long double>::min)();\n  //    EXPECT_EQ(fmt::format(\"{:.0}\", ld), \"3e-4932\");\n  //    EXPECT_EQ(\n  //        fmt::format(\"{:0g}\", std::numeric_limits<long double>::denorm_min()),\n  //        \"3.6452e-4951\");\n  //  }\n\n  CHECK(emio::format(\"{:#.0f}\", 123.0) == \"123.\");\n  CHECK(emio::format(\"{:.02f}\", 1.234) == \"1.23\");\n  CHECK(emio::format(\"{:.1g}\", 0.001) == \"0.001\");\n  CHECK(emio::format(\"{}\", 1019666432.0f) == \"1019666432\");  // Different from FMT: 1019666400 but not totally wrong.\n  CHECK(emio::format(\"{:.0e}\", 9.5) == \"1e+01\");\n  CHECK(emio::format(\"{:.1e}\", 1e-34) == \"1.0e-34\");\n\n  CHECK(emio::format(emio::runtime(\"{0:.2}\"), reinterpret_cast<void*>(0xcafe)) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2f}\"), reinterpret_cast<void*>(0xcafe)) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.2e}\"), reinterpret_cast<void*>(0xcafe)) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.-1e}\"), 42.0) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{0:.-1e}\"), 42.0) == emio::err::invalid_format);\n\n  //  CHECK(emio::format(\"{0:.2}\", \"str\") == \"st\");\n  //  CHECK(emio::format(\"{0:.5}\", \"вожыкі\") == \"вожык\");\n}\n\nTEST_CASE(\"format bool\") {\n  CHECK(emio::format(\"{}\", true) == \"true\");\n  CHECK(emio::format(\"{}\", false) == \"false\");\n  CHECK(emio::format(\"{:d}\", true) == \"1\");\n  CHECK(emio::format(\"{:5}\", true) == \"true \");\n  CHECK(emio::format(\"{:s}\", true) == \"true\");\n  CHECK(emio::format(\"{:s}\", false) == \"false\");\n  CHECK(emio::format(\"{:6s}\", false) == \"false \");\n}\n\nTEST_CASE(\"format short\") {\n  short s = 42;\n  CHECK(emio::format(\"{0:d}\", s) == \"42\");\n  unsigned short us = 42;\n  CHECK(emio::format(\"{0:d}\", us) == \"42\");\n}\n\nTEST_CASE(\"format int\") {\n  CHECK(emio::format(emio::runtime(\"{0:v\"), 42) == emio::err::invalid_format);\n  // check_unknown_types(42, \"bBdoxXnLc\", \"integer\");\n  CHECK(emio::format(\"{:c}\", static_cast<int>('x')) == \"x\");\n}\n\nTEST_CASE(\"format bin\") {\n  CHECK(emio::format(\"{0:b}\", 0) == \"0\");\n  CHECK(emio::format(\"{0:b}\", 42) == \"101010\");\n  CHECK(emio::format(\"{0:b}\", 42u) == \"101010\");\n  CHECK(emio::format(\"{0:b}\", -42) == \"-101010\");\n  CHECK(emio::format(\"{0:b}\", 12345) == \"11000000111001\");\n  CHECK(emio::format(\"{0:b}\", 0x12345678) == \"10010001101000101011001111000\");\n  CHECK(emio::format(\"{0:b}\", 0x90ABCDEF) == \"10010000101010111100110111101111\");\n  CHECK(emio::format(\"{0:b}\", std::numeric_limits<uint32_t>::max()) == \"11111111111111111111111111111111\");\n}\n\nTEST_CASE(\"format dec\") {\n  CHECK(emio::format(\"{0}\", 0) == \"0\");\n  CHECK(emio::format(\"{0}\", 42) == \"42\");\n  CHECK(emio::format(\"{:}>\", 42) == \"42>\");\n  CHECK(emio::format(\"{0:d}\", 42) == \"42\");\n  CHECK(emio::format(\"{0}\", 42u) == \"42\");\n  CHECK(emio::format(\"{0}\", -42) == \"-42\");\n  CHECK(emio::format(\"{0}\", 12345) == \"12345\");\n  CHECK(emio::format(\"{0}\", 67890) == \"67890\");\n  //  #if FMT_USE_INT128\n  //  CHECK(emio::format(\"{0}\", static_cast<__int128_t>(0)) == \"0\");\n  //  CHECK(emio::format(\"{0}\", static_cast<__uint128_t>(0)) == \"0\");\n  //  CHECK(emio::format(\"{0}\", static_cast<__int128_t>(INT64_MAX) + 1) == \"9223372036854775808\");\n  //  CHECK(emio::format(\"{0}\", static_cast<__int128_t>(INT64_MIN) - 1) == \"-9223372036854775809\");\n  //  CHECK(emio::format(\"{0}\", static_cast<__int128_t>(UINT64_MAX) + 1) == \"18446744073709551616\");\n  //  CHECK(emio::format(\"{0}\", int128_max) == \"170141183460469231731687303715884105727\");\n  //  CHECK(emio::format(\"{0}\", int128_min) == \"-170141183460469231731687303715884105728\");\n  //  CHECK(emio::format(\"{0}\", uint128_max) == \"340282366920938463463374607431768211455\");\n  //  #endif\n\n  /*char buffer[buffer_size];\n  safe_sprintf(buffer, \"%d\", INT_MIN);\n  CHECK(emio::format(\"{0}\", INT_MIN) == buffer);\n  safe_sprintf(buffer, \"%d\", INT_MAX);\n  CHECK(emio::format(\"{0}\", INT_MAX) == buffer);\n  safe_sprintf(buffer, \"%u\", UINT_MAX);\n  CHECK(emio::format(\"{0}\", UINT_MAX) == buffer);\n  safe_sprintf(buffer, \"%ld\", 0 - static_cast<unsigned long>(LONG_MIN));\n  CHECK(emio::format(\"{0}\", LONG_MIN) == buffer);\n  safe_sprintf(buffer, \"%ld\", LONG_MAX);\n  CHECK(emio::format(\"{0}\", LONG_MAX) == buffer);\n  safe_sprintf(buffer, \"%lu\", ULONG_MAX);\n  CHECK(emio::format(\"{0}\", ULONG_MAX) == buffer);*/\n}\n\nTEST_CASE(\"format hex\") {\n  CHECK(emio::format(\"{0:x}\", 0) == \"0\");\n  CHECK(emio::format(\"{0:x}\", 0x42) == \"42\");\n  CHECK(emio::format(\"{0:x}\", 0x42u) == \"42\");\n  CHECK(emio::format(\"{0:x}\", -0x42) == \"-42\");\n  CHECK(emio::format(\"{0:x}\", 0x12345678) == \"12345678\");\n  CHECK(emio::format(\"{0:x}\", 0x90abcdef) == \"90abcdef\");\n  CHECK(emio::format(\"{0:X}\", 0x12345678) == \"12345678\");\n  CHECK(emio::format(\"{0:X}\", 0x90ABCDEF) == \"90ABCDEF\");\n  //  #if FMT_USE_INT128\n  //  CHECK(emio::format(\"{0:x}\", static_cast<__int128_t>(0)) == \"0\");\n  //  CHECK(emio::format(\"{0:x}\", static_cast<__uint128_t>(0)) == \"0\");\n  //  CHECK(emio::format(\"{0:x}\", static_cast<__int128_t>(INT64_MAX) + 1) == \"8000000000000000\");\n  //  CHECK(emio::format(\"{0:x}\", static_cast<__int128_t>(INT64_MIN) - 1) == \"-8000000000000001\");\n  //  CHECK(emio::format(\"{0:x}\", static_cast<__int128_t>(UINT64_MAX) + 1) == \"10000000000000000\");\n  //  CHECK(emio::format(\"{0:x}\", int128_max) == \"7fffffffffffffffffffffffffffffff\");\n  //  CHECK(emio::format(\"{0:x}\", int128_min) == \"-80000000000000000000000000000000\");\n  //  CHECK(emio::format(\"{0:x}\", uint128_max) == \"ffffffffffffffffffffffffffffffff\");\n  //  #endif\n\n  /*char buffer[buffer_size];\n  safe_sprintf(buffer, \"-%x\", 0 - static_cast<unsigned>(INT_MIN));\n  CHECK(emio::format(\"{0:x}\", INT_MIN) == buffer);\n  safe_sprintf(buffer, \"%x\", INT_MAX);\n  CHECK(emio::format(\"{0:x}\", INT_MAX) == buffer);\n  safe_sprintf(buffer, \"%x\", UINT_MAX);\n  CHECK(emio::format(\"{0:x}\", UINT_MAX) == buffer);\n  safe_sprintf(buffer, \"-%lx\", 0 - static_cast<unsigned long>(LONG_MIN));\n  CHECK(emio::format(\"{0:x}\", LONG_MIN) == buffer);\n  safe_sprintf(buffer, \"%lx\", LONG_MAX);\n  CHECK(emio::format(\"{0:x}\", LONG_MAX) == buffer);\n  safe_sprintf(buffer, \"%lx\", ULONG_MAX);\n  CHECK(emio::format(\"{0:x}\", ULONG_MAX) == buffer);*/\n}\n\nTEST_CASE(\"format_oct\") {\n  CHECK(emio::format(\"{0:o}\", 0) == \"0\");\n  CHECK(emio::format(\"{0:o}\", 042) == \"42\");\n  CHECK(emio::format(\"{0:o}\", 042u) == \"42\");\n  CHECK(emio::format(\"{0:o}\", -042) == \"-42\");\n  CHECK(emio::format(\"{0:o}\", 012345670) == \"12345670\");\n  /*#if FMT_USE_INT128\n    CHECK(emio::format(\"{0:o}\", static_cast<__int128_t>(0)) == \"0\");\n    CHECK(emio::format(\"{0:o}\", static_cast<__uint128_t>(0)) == \"0\");\n    CHECK(emio::format(\"{0:o}\", static_cast<__int128_t>(INT64_MAX) + 1) == \"1000000000000000000000\");\n    CHECK(emio::format(\"{0:o}\", static_cast<__int128_t>(INT64_MIN) - 1) == \"-1000000000000000000001\");\n    CHECK(emio::format(\"{0:o}\", static_cast<__int128_t>(UINT64_MAX) + 1) == \"2000000000000000000000\");\n    CHECK(emio::format(\"{0:o}\", int128_max) == \"1777777777777777777777777777777777777777777\");\n    CHECK(emio::format(\"{0:o}\", int128_min) == \"-2000000000000000000000000000000000000000000\");\n    CHECK(emio::format(\"{0:o}\", uint128_max) == \"3777777777777777777777777777777777777777777\");\n  #endif*/\n\n  /*char buffer[buffer_size];\n  safe_sprintf(buffer, \"-%o\", 0 - static_cast<unsigned>(INT_MIN));\n  CHECK(emio::format(\"{0:o}\", INT_MIN) == buffer);\n  safe_sprintf(buffer, \"%o\", INT_MAX);\n  CHECK(emio::format(\"{0:o}\", INT_MAX) == buffer);\n  safe_sprintf(buffer, \"%o\", UINT_MAX);\n  CHECK(emio::format(\"{0:o}\", UINT_MAX) == buffer);\n  safe_sprintf(buffer, \"-%lo\", 0 - static_cast<unsigned long>(LONG_MIN));\n  CHECK(emio::format(\"{0:o}\", LONG_MIN) == buffer);\n  safe_sprintf(buffer, \"%lo\", LONG_MAX);\n  CHECK(emio::format(\"{0:o}\", LONG_MAX) == buffer);\n  safe_sprintf(buffer, \"%lo\", ULONG_MAX);\n  CHECK(emio::format(\"{0:o}\", ULONG_MAX) == buffer);*/\n}\n\nTEST_CASE(\"format_double\") {\n  CHECK(emio::format(\"{}\", 0.0) == \"0\");\n  //  check_unknown_types(1.2, \"eEfFgGaAnL%\", \"double\");\n  CHECK(emio::format(\"{:}\", 0.0) == \"0\");\n  CHECK(emio::format(\"{:f}\", 0.0) == \"0.000000\");\n  CHECK(emio::format(\"{:g}\", 0.0) == \"0\");\n  CHECK(emio::format(\"{:}\", 392.65) == \"392.65\");\n  CHECK(emio::format(\"{:g}\", 392.65) == \"392.65\");\n  CHECK(emio::format(\"{:G}\", 392.65) == \"392.65\");\n  CHECK(emio::format(\"{:g}\", 4.9014e6) == \"4.9014e+06\");\n  CHECK(emio::format(\"{:f}\", 392.65) == \"392.650000\");\n  CHECK(emio::format(\"{:F}\", 392.65) == \"392.650000\");\n  //  CHECK(emio::format(\"{:L}\", 42.0) == \"42\");\n  //  CHECK(emio::format(\"{:24a}\", 4.2f) == \"           0x1.0cccccp+2\");\n  //  CHECK(emio::format(\"{:24a}\", 4.2) == \"    0x1.0cccccccccccdp+2\");\n  //  CHECK(emio::format(\"{:<24a}\", 4.2) == \"0x1.0cccccccccccdp+2    \");\n  CHECK(emio::format(\"{0:e}\", 392.65) == \"3.926500e+02\");\n  CHECK(emio::format(\"{0:E}\", 392.65) == \"3.926500E+02\");\n  CHECK(emio::format(\"{0:+010.4g}\", 392.65) == \"+0000392.6\");\n\n  //  char buffer[buffer_size];\n  //  double xd = 0x1.ffffffffffp+2;\n  //  safe_sprintf(buffer, \"%.*a\", 10, xd);\n  //  CHECK(emio::format(\"{:.10a}\", xd) == buffer);\n  //  safe_sprintf(buffer, \"%.*a\", 9, xd);\n  //  CHECK(emio::format(\"{:.9a}\", xd) == buffer);\n\n  //  if (std::numeric_limits<long double>::digits == 64) {\n  //    auto ld = 0xf.ffffffffffp-3l;\n  //    safe_sprintf(buffer, \"%La\", ld);\n  //    CHECK(emio::format(\"{:a}\", ld) == buffer);\n  //    safe_sprintf(buffer, \"%.*La\", 10, ld);\n  //    CHECK(emio::format(\"{:.10a}\", ld) == buffer);\n  //    safe_sprintf(buffer, \"%.*La\", 9, ld);\n  //    CHECK(emio::format(\"{:.9a}\", ld) == buffer);\n  //  }\n\n  //  if (fmt::detail::const_check(std::numeric_limits<double>::is_iec559)) {\n  //    double d = (std::numeric_limits<double>::min)();\n  //    CHECK(emio::format(\"{:a}\", d) == \"0x1p-1022\");\n  //    CHECK(emio::format(\"{:#a}\", d) == \"0x1.p-1022\");\n  //\n  //    d = (std::numeric_limits<double>::max)();\n  //    safe_sprintf(buffer, \"%a\", d);\n  //    CHECK(emio::format(\"{:a}\", d) == buffer);\n  //\n  //    d = std::numeric_limits<double>::denorm_min();\n  //    CHECK(emio::format(\"{:a}\", d) == \"0x0.0000000000001p-1022\");\n  //  }\n\n  //  safe_sprintf(buffer, \"%.*a\", 10, 4.2);\n  //  CHECK(emio::format(\"{:.10a}\", 4.2) == buffer);\n  //\n  //  CHECK(emio::format(\"{:a}\", -42.0) == \"-0x1.5p+5\");\n  //  CHECK(emio::format(\"{:A}\", -42.0) == \"-0X1.5P+5\");\n\n  CHECK(emio::format(\"{:f}\", 9223372036854775807.0) == \"9223372036854775808.000000\");\n}\n\nTEST_CASE(\"precision_rounding\") {\n  CHECK(emio::format(\"{:.0f}\", 0.0) == \"0\");\n  CHECK(emio::format(\"{:.0f}\", 0.01) == \"0\");\n  CHECK(emio::format(\"{:.0f}\", 0.1) == \"0\");\n  CHECK(emio::format(\"{:.3f}\", 0.00049) == \"0.000\");\n  CHECK(emio::format(\"{:.3f}\", 0.0005) == \"0.001\");\n  CHECK(emio::format(\"{:.3f}\", 0.00149) == \"0.001\");\n  CHECK(emio::format(\"{:.3f}\", 0.0015) == \"0.002\");\n  CHECK(emio::format(\"{:.3f}\", 0.9999) == \"1.000\");\n  CHECK(emio::format(\"{:.3}\", 0.00123) == \"0.00123\");\n  CHECK(emio::format(\"{:.16g}\", 0.1) == \"0.1\");\n  CHECK(emio::format(\"{:.0}\", 1.0) == \"1\");\n  CHECK(emio::format(\"{:.17f}\", 225.51575035152064) == \"225.51575035152063720\");\n  CHECK(emio::format(\"{:.1f}\", -761519619559038.2) == \"-761519619559038.2\");\n  CHECK(emio::format(\"{}\", 1.9156918820264798e-56) == \"1.9156918820264798e-56\");\n  CHECK(emio::format(\"{:.4f}\", 7.2809479766055470e-15) == \"0.0000\");\n\n  // Trigger a rounding error in Grisu by a specially chosen number.\n  CHECK(emio::format(\"{:f}\", 3788512123356.985352) == \"3788512123356.985352\");\n}\n\nTEST_CASE(\"prettify_float\") {\n  CHECK(emio::format(\"{}\", 1e-4) == \"0.0001\");\n  CHECK(emio::format(\"{}\", 1e-5) == \"1e-05\");\n  CHECK(emio::format(\"{}\", 1e15) == \"1000000000000000\");\n  CHECK(emio::format(\"{}\", 1e16) == \"1e+16\");\n  CHECK(emio::format(\"{}\", 9.999e-5) == \"9.999e-05\");\n  CHECK(emio::format(\"{}\", 1e10) == \"10000000000\");\n  CHECK(emio::format(\"{}\", 1e11) == \"100000000000\");\n  CHECK(emio::format(\"{}\", 1234e7) == \"12340000000\");\n  CHECK(emio::format(\"{}\", 1234e-2) == \"12.34\");\n  CHECK(emio::format(\"{}\", 1234e-6) == \"0.001234\");\n  //  CHECK(emio::format(\"{}\", 0.1f) == \"0.1\"); -> float not supported\n  CHECK(emio::format(\"{}\", double(0.1f)) == \"0.10000000149011612\");\n  //  CHECK(emio::format(\"{}\", 1.35631564e-19f) == \"1.3563156e-19\"); -> float not supported\n  //  printf(\"%.15e %.15e\\r\\n\", 1.35631564e-19f, 1.35631564e-19);\n}\n\nTEST_CASE(\"format_nan\") {\n  double nan = std::numeric_limits<double>::quiet_NaN();\n  CHECK(emio::format(\"{}\", nan) == \"nan\");\n  CHECK(emio::format(\"{:+}\", nan) == \"+nan\");\n  CHECK(emio::format(\"{:+06}\", nan) == \"  +nan\");\n  CHECK(emio::format(\"{:<+06}\", nan) == \"+nan  \");\n  CHECK(emio::format(\"{:^+06}\", nan) == \" +nan \");\n  CHECK(emio::format(\"{:>+06}\", nan) == \"  +nan\");\n  if (std::signbit(-nan)) {\n    CHECK(emio::format(\"{}\", -nan) == \"-nan\");\n    CHECK(emio::format(\"{:+06}\", -nan) == \"  -nan\");\n  } else {\n    WARN(\"compiler doesn't handle negative NaN correctly\");\n  }\n  CHECK(emio::format(\"{: }\", nan) == \" nan\");\n  CHECK(emio::format(\"{:F}\", nan) == \"NAN\");\n  CHECK(emio::format(\"{:<7}\", nan) == \"nan    \");\n  CHECK(emio::format(\"{:^7}\", nan) == \"  nan  \");\n  CHECK(emio::format(\"{:>7}\", nan) == \"    nan\");\n  CHECK(emio::format(\"{:07}\", nan) == \"    nan\");\n  CHECK(emio::format(\"{:x>07}\", nan) == \"xxxxnan\");\n}\n\nTEST_CASE(\"format_infinity\") {\n  double inf = std::numeric_limits<double>::infinity();\n  CHECK(emio::format(\"{}\", inf) == \"inf\");\n  CHECK(emio::format(\"{:+}\", inf) == \"+inf\");\n  CHECK(emio::format(\"{}\", -inf) == \"-inf\");\n  CHECK(emio::format(\"{:+06}\", inf) == \"  +inf\");\n  CHECK(emio::format(\"{:+06}\", -inf) == \"  -inf\");\n  CHECK(emio::format(\"{:<+06}\", inf) == \"+inf  \");\n  CHECK(emio::format(\"{:^+06}\", inf) == \" +inf \");\n  CHECK(emio::format(\"{:>+06}\", inf) == \"  +inf\");\n  CHECK(emio::format(\"{: }\", inf) == \" inf\");\n  CHECK(emio::format(\"{:F}\", inf) == \"INF\");\n  CHECK(emio::format(\"{:<7}\", inf) == \"inf    \");\n  CHECK(emio::format(\"{:^7}\", inf) == \"  inf  \");\n  CHECK(emio::format(\"{:>7}\", inf) == \"    inf\");\n  CHECK(emio::format(\"{:07}\", inf) == \"    inf\");\n  CHECK(emio::format(\"{:x>07}\", inf) == \"xxxxinf\");\n}\n\nTEST_CASE(\"format_char\") {\n  //  const char types[] = \"cbBdoxX\";\n  //  check_unknown_types('a', types, \"char\");\n  CHECK(emio::format(\"{0}\", 'a') == \"a\");\n  CHECK(emio::format(\"{0:c}\", 'z') == \"z\");\n  CHECK(emio::format(\"{0:d}\", '0') == \"48\");\n  CHECK(emio::format(\"{0:x}\", '0') == \"30\");\n  //  int n = 'x';\n  //  for (const char* type = types + 1; *type; ++type) {\n  //    std::string format_str = fmt::format(\"{{:{}}}\", *type);\n  //    EXPECT_EQ(fmt::format(runtime(format_str), n),\n  //              fmt::format(runtime(format_str), 'x'))\n  //        << format_str;\n  //  }\n  //  CHECK(emio::format(\"{:02X}\", 'x') == fmt::format(\"{:02X}\", n));\n\n  CHECK(emio::format(\"{}\", '\\n') == \"\\n\");\n  CHECK(emio::format(\"{:?}\", '\\n') == \"'\\\\n'\");\n}\n\nTEST_CASE(\"format_volatile_char\") {\n  //  volatile char c = 'x';\n  // TODO: CHECK(emio::format(\"{}\", c) == \"x\");\n}\n\nTEST_CASE(\"format_unsigned_char\") {\n  CHECK(emio::format(\"{}\", static_cast<unsigned char>(42)) == \"42\");\n  CHECK(emio::format(\"{}\", static_cast<uint8_t>(42)) == \"42\");\n}\n\nTEST_CASE(\"format_cstring\") {\n  // check_unknown_types(\"test\", \"sp\", \"string\");\n  CHECK(emio::format(\"{0}\", \"test\") == \"test\");\n  CHECK(emio::format(\"{0:s}\", \"test\") == \"test\");\n  char nonconst[] = \"nonconst\";\n  CHECK(emio::format(\"{0}\", nonconst) == \"nonconst\");\n  //  EXPECT_THROW_MSG(\n  //      (void)fmt::format(runtime(\"{0}\"), static_cast<const char*>(nullptr)),\n  //      format_error, \"string pointer is null\");\n}\n\nvoid function_pointer_test(int, double, std::string) {}\n\nTEST_CASE(\"format_pointer\") {\n  // check_unknown_types(reinterpret_cast<void*>(0x1234), \"p\", \"pointer\");\n  CHECK(emio::format(\"{0}\", static_cast<void*>(nullptr)) == \"0x0\");\n  CHECK(emio::format(\"{0}\", reinterpret_cast<void*>(0x1234)) == \"0x1234\");\n  CHECK(emio::format(\"{0:p}\", reinterpret_cast<void*>(0x1234)) == \"0x1234\");\n  CHECK(emio::format(\"{0}\", reinterpret_cast<void*>(~uintptr_t())) ==\n        (\"0x\" + std::string(sizeof(void*) * CHAR_BIT / 4, 'f')));\n  // CHECK(emio::format(\"{}\", fmt::ptr(reinterpret_cast<int*>(0x1234))) == \"0x1234\");\n\n  // std::unique_ptr<int> up(new int(1));\n  // CHECK(emio::format(\"{}\", fmt::ptr(up.get())), fmt::format(\"{}\", fmt::ptr(up)));\n  // std::shared_ptr<int> sp(new int(1));\n  // CHECK(ptr(sp.get())), fmt::format(\"{}\", fmt::ptr(sp)) == fmt::format(\"{}\");\n  // EXPECT_EQ(fmt::format(\"{}\", fmt::detail::bit_cast<const void*>(&function_pointer_test)), fmt::format(\"{}\",\n  // fmt::ptr(function_pointer_test)));\n  CHECK(emio::format(\"{}\", nullptr) == \"0x0\");\n\n  // Const and/or volatile.\n  static_assert(std::is_same_v<std::remove_cv_t<void* const>, void*>);\n  CHECK(emio::format(\"{}\", reinterpret_cast<const void*>(0x456)) == \"0x456\");\n  CHECK(emio::format(\"{}\", reinterpret_cast<volatile void*>(0x789)) == \"0x789\");\n  CHECK(emio::format(\"{}\", reinterpret_cast<const volatile void*>(0x101112)) == \"0x101112\");\n\n  // With emio::ptr\n  int* i1{};\n  volatile int* i2{};\n  const int* i3{};\n  const volatile int* i4{};\n  std::unique_ptr<int> u;\n  std::shared_ptr<int> s;\n\n  CHECK(emio::format(\"{}\", emio::ptr(i1)) == \"0x0\");\n  CHECK(emio::format(\"{}\", emio::ptr(i2)) == \"0x0\");\n  CHECK(emio::format(\"{}\", emio::ptr(i3)) == \"0x0\");\n  CHECK(emio::format(\"{}\", emio::ptr(i4)) == \"0x0\");\n  CHECK(emio::format(\"{}\", emio::ptr(u)) == \"0x0\");\n  CHECK(emio::format(\"{}\", emio::ptr(s)) == \"0x0\");\n}\n\nenum color { red, green, blue };\n\nTEST_CASE(\"format_enum\") {\n  CHECK(emio::format(\"{}\", static_cast<std::underlying_type_t<color>>(color::red)) == \"0\");\n  CHECK(emio::format(\"{}\", color::red) == \"0\");\n}\n\nTEST_CASE(\"format_byte\") {\n  std::byte b{5};\n  CHECK(emio::format(\"{}\", b) == \"5\");\n}\n\nTEST_CASE(\"format_string\") {\n  CHECK(emio::format(\"{}\", std::string(\"test\")) == \"test\");\n  CHECK(emio::format(\"{:s}\", std::string(\"test\")) == \"test\");\n  CHECK(emio::format(\"{:?}\", std::string(\"test\")) == \"\\\"test\\\"\");\n  CHECK(emio::format(\"{:*^10?}\", std::string(\"test\")) == \"**\\\"test\\\"**\");\n  CHECK(emio::format(\"{:?}\", std::string(\"\\test\")) == \"\\\"\\\\test\\\"\");\n\n  CHECK(emio::format(emio::runtime(\"{:x}\"), std::string(\"test\")) == emio::err::invalid_format);\n}\n\nTEST_CASE(\"format_string_view\") {\n  CHECK(emio::format(\"{}\", std::string_view(\"test\")) == \"test\");\n  CHECK(emio::format(\"{:s}\", std::string_view(\"test\")) == \"test\");\n  CHECK(emio::format(\"{:.0}\", std::string_view(\"test\")) == \"\");\n  CHECK(emio::format(\"{:.2}\", std::string_view(\"test\")) == \"te\");\n  CHECK(emio::format(\"{:.4}\", std::string_view(\"test\")) == \"test\");\n  CHECK(emio::format(\"{:.6}\", std::string_view(\"test\")) == \"test\");\n  CHECK(emio::format(\"{:?}\", std::string_view(\"t\\nst\")) == \"\\\"t\\\\nst\\\"\");\n  CHECK(emio::format(\"{}\", std::string_view()) == \"\");\n  CHECK(emio::format(\"{:?}\", std::string_view(\"t\\n\\r\\t\\\\\\'\\\"st\")) == \"\\\"t\\\\n\\\\r\\\\t\\\\\\\\\\\\'\\\\\\\"st\\\"\");\n  CHECK(emio::format(\"{:?}\", std::string_view(\"\\x05\\xab\\xEf\")) == \"\\\"\\\\x05\\\\xab\\\\xef\\\"\");\n\n  CHECK(!validate_format_string<std::string_view>(\"{:.3?}\"sv));\n}\n\n#ifdef FMT_USE_STRING_VIEW\nstruct string_viewable {};\n\nFMT_BEGIN_NAMESPACE\ntemplate <>\nstruct formatter<string_viewable> : formatter<std::string_view> {\n  auto format(string_viewable, format_context& ctx) -> decltype(ctx.out()) {\n    return formatter<std::string_view>::format(\"foo\", ctx);\n  }\n};\nFMT_END_NAMESPACE\n\nTEST(format_test, format_std_string_view) {\n  CHECK(emio::format(\"{}\", std::string_view(\"test\")) == \"test\");\n  CHECK(emio::format(\"{}\", string_viewable()) == \"foo\");\n}\n\nstruct explicitly_convertible_to_std_string_view {\n  explicit operator std::string_view() const {\n    return \"foo\";\n  }\n};\n\ntemplate <>\nstruct fmt::formatter<explicitly_convertible_to_std_string_view> : formatter<std::string_view> {\n  auto format(explicitly_convertible_to_std_string_view v, format_context& ctx) -> decltype(ctx.out()) {\n    return format_to(ctx.out(), \"'{}'\", std::string_view(v));\n  }\n};\n\nTEST(format_test, format_explicitly_convertible_to_std_string_view) {\n  CHECK(emio::format(\"{}\", explicitly_convertible_to_std_string_view()) == \"'foo'\");\n}\n#endif\n\nTEST_CASE(\"format at compile-time\") {\n  constexpr bool success = [] {\n    emio::static_buffer<17> buf{};\n\n    emio::result<void> res = emio::format_to(buf, \"{} {:.2f} {}{}\", 42, 42.24, \"x,\", 'y');\n    return res && buf.view() == \"42 42.24 x,y\";\n  }();\n  STATIC_CHECK(success);\n}\n\nTEST_CASE(\"validate_format_string\") {\n  CHECK(validate_format_string(\"\"sv));\n  CHECK(validate_format_string(\"abc\"sv));\n  CHECK(!validate_format_string(\"abc{\"sv));\n  CHECK(!validate_format_string(\"abc{x\"sv));\n  CHECK(validate_format_string(\"abc{{\"sv));\n  CHECK(!validate_format_string(\"abc}\"sv));\n  CHECK(!validate_format_string(\"abc}x\"sv));\n  CHECK(validate_format_string(\"abc}}\"sv));\n  CHECK(validate_format_string<int>(\"abc{}\"sv));\n  CHECK(validate_format_string<int>(\"abc{0}\"sv));\n  CHECK(!validate_format_string<int>(\"abc{1}\"sv));\n  CHECK(!validate_format_string<int, short>(\"abc{0}{}\"sv));\n  CHECK(!validate_format_string<int, short>(\"abc{}{1}\"sv));\n  CHECK(validate_format_string<int, short>(\"abc{0}{1}\"sv));\n  CHECK(validate_format_string<int>(\"abc{:}\"sv));\n  CHECK(!validate_format_string<int>(\"abc{:f}\"sv));\n  CHECK(!validate_format_string<int>(\"abc\"sv));\n\n  CHECK(validate_format_string<int>(\"{:<10}\"sv));\n  CHECK(validate_format_string<int>(\"{:^10}\"sv));\n  CHECK(validate_format_string<int>(\"{:>10}\"sv));\n  CHECK(validate_format_string<int>(\"{:<<10}\"sv));\n  CHECK(validate_format_string<int>(\"{:^^10}\"sv));\n  CHECK(validate_format_string<int>(\"{:>>10}\"sv));\n  CHECK(validate_format_string<int>(\"{:<}\"sv));\n\n  CHECK(validate_format_string<int>(\"{:a<10}\"sv));\n  CHECK(validate_format_string<int>(\"{:a^10}\"sv));\n  CHECK(validate_format_string<int>(\"{:a>10}\"sv));\n  CHECK(!validate_format_string<int>(\"{:a=10}\"sv));\n  CHECK(validate_format_string<int>(\"{:a<}\"sv));\n  CHECK(validate_format_string<int>(\"{:a<010}\"sv));\n\n  CHECK(validate_format_string<int>(\"{:a<-10}\"sv));\n  CHECK(validate_format_string<int>(\"{:a<+10}\"sv));\n  CHECK(validate_format_string<int>(\"{:a< 10}\"sv));\n  CHECK(validate_format_string<int>(\"{:a<-#10}\"sv));\n  CHECK(!validate_format_string<int>(\"{:a<--10}\"sv));\n\n  CHECK(validate_format_string<int>(\"{:+}\"sv));\n  CHECK(validate_format_string<int>(\"{:-}\"sv));\n  CHECK(validate_format_string<int>(\"{: }\"sv));\n  CHECK(validate_format_string<int>(\"{:<}\"sv));\n\n  CHECK(validate_format_string<int>(\"{:+10}\"sv));\n  CHECK(validate_format_string<int>(\"{:-10}\"sv));\n  CHECK(validate_format_string<int>(\"{: 10}\"sv));\n\n  CHECK(validate_format_string<int>(\"{:+#10}\"sv));\n  CHECK(validate_format_string<int>(\"{:-#10}\"sv));\n  CHECK(validate_format_string<int>(\"{: #10}\"sv));\n\n  CHECK(validate_format_string<int>(\"{:010}\"sv));\n  CHECK(validate_format_string<int>(\"{:-010}\"sv));\n  CHECK(validate_format_string<int>(\"{:-#010}\"sv));\n  CHECK(validate_format_string<int>(\"{:0010}\"sv));\n  CHECK(validate_format_string<int>(\"{:0}\"sv));\n  CHECK(!validate_format_string<int>(\"{:--010}\"sv));\n\n  CHECK(!validate_format_string<int>(\"{:{}}\"sv));\n  CHECK(!validate_format_string<int>(\"{:s}\"sv));\n  CHECK(validate_format_string<double>(\"{:.1100}\"sv));\n  CHECK(!validate_format_string<double>(\"{:.1101}\"sv));\n  CHECK(!validate_format_string<double>(\"{:.2147483648}\"sv));\n  CHECK(!validate_format_string<double>(\"{:2147483648}\"sv));\n  CHECK(!validate_format_string<double>(\"{:.+1}\"sv));\n  CHECK(!validate_format_string<double>(\"{:.-1}\"sv));\n  CHECK(!validate_format_string<double>(\"{:x}\"sv));\n\n  CHECK(validate_format_string<bool>(\"{}\"sv));\n  CHECK(validate_format_string<bool>(\"{:d}\"sv));\n  CHECK(!validate_format_string<bool>(\"{:f}\"sv));\n  CHECK(!validate_format_string<bool>(\"{:.5}\"sv));\n\n  CHECK(validate_format_string<char>(\"{}\"sv));\n  CHECK(validate_format_string<char>(\"{:d}\"sv));\n\n  CHECK(validate_format_string<int>(\"{}\"sv));\n  CHECK(validate_format_string<unsigned>(\"{}\"sv));\n\n  CHECK(validate_format_string<void*>(\"{}\"sv));\n}\n"
  },
  {
    "path": "test/unit_test/test_format_api.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namespace std::string_view_literals;\n\nTEST_CASE(\"emio::format\", \"[format]\") {\n  // Test strategy:\n  // * Call emio::format.\n  // Expected: The return types, values and the format results are correct.\n\n  SECTION(\"success\") {\n    std::string res = emio::format(\"{}\", 42);\n    CHECK(res == \"42\");\n  }\n  SECTION(\"invalid_format\") {\n    emio::result<std::string> res = emio::format(emio::runtime(\"{\"), 42);\n    CHECK(res == emio::err::invalid_format);\n  }\n}\n\nTEST_CASE(\"emio::vformat\", \"[format]\") {\n  // Test strategy:\n  // * Call emio::vformat.\n  // Expected: The return types, values and the format results are correct.\n\n  SECTION(\"success\") {\n    emio::result<std::string> res = emio::vformat(emio::make_format_args(\"{}\", 42));\n    CHECK(res == \"42\");\n  }\n  SECTION(\"invalid_format\") {\n    emio::result<std::string> res = emio::vformat(emio::make_format_args(emio::runtime(\"{\"), 42));\n    CHECK(res == emio::err::invalid_format);\n  }\n}\n"
  },
  {
    "path": "test/unit_test/test_format_as.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nnamespace foo {\n\nenum class bar {};\n\nconstexpr auto format_as(const bar& w) {\n  return static_cast<std::underlying_type_t<bar>>(w);\n}\n\n}  // namespace foo\n\nTEST_CASE(\"format_as\", \"[formatter]\") {\n  // Test strategy:\n  // * Format a custom type with a format_as.\n  // Expected: The formatting works.\n\n  SECTION(\"compile-time\") {\n    SECTION(\"simple\") {\n      constexpr bool success = [] {\n        emio::static_buffer<10> buf{};\n\n        static_cast<void>(emio::format_to(buf, \"{}\", foo::bar{42}).value());\n        return buf.view() == \"42\";\n      }();\n      STATIC_CHECK(success);\n    }\n    SECTION(\"complex\") {\n      constexpr bool success = [] {\n        emio::static_buffer<10> buf{};\n\n        static_cast<void>(emio::format_to(buf, \"{:x<4x}\", foo::bar{42}).value());\n        return buf.view() == \"2axx\";\n      }();\n      STATIC_CHECK(success);\n    }\n  }\n\n  CHECK(emio::format(\"{}\", foo::bar{42}) == \"42\");\n  CHECK(emio::format(\"{:x<4x}\", foo::bar{42}) == \"2axx\");\n}\n"
  },
  {
    "path": "test/unit_test/test_format_could_fail_api.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namespace std::string_view_literals;\n\nTEST_CASE(\"format format_args which could fail\", \"[format]\") {\n  // Test strategy:\n  // * Call emio's format function with format_args as parameters.\n  // Expected: format_args can be formatted or if not, the correct error is returned.\n\n  const auto check = [](const auto& args) {\n    // Check if args is a format_args.\n    const emio::format_args& base_args = args;\n    STATIC_CHECK(std::is_base_of_v<emio::format_args, std::decay_t<decltype(args)>>);\n    STATIC_CHECK(!std::is_same_v<emio::format_args, std::decay_t<decltype(args)>>);\n\n    CHECK(emio::formatted_size(\"{}\", args).value() == 3);\n    CHECK(emio::formatted_size(\"{}\", base_args).value() == 3);\n    CHECK(emio::formatted_size(\"{} {}\", args, base_args).value() == 7);\n    CHECK(emio::formatted_size(\"{}\", emio::make_format_args(\"{}\", emio::make_format_args(\"{}\", args))).value() == 3);\n    CHECK(emio::formatted_size(\"{}\", emio::make_format_args(\"{}\", 123)).value() == 3);\n    CHECK(emio::formatted_size(\"{}\", emio::make_format_args(emio::runtime(\"{}\"), 123)).value() == 3);\n    CHECK(emio::formatted_size(\"{}\", emio::make_format_args(emio::runtime(\"{}\"))) == emio::err::invalid_format);\n    CHECK(emio::formatted_size(\"{}\", emio::make_format_args(emio::runtime(\"{\"), 123)) == emio::err::invalid_format);\n\n    CHECK(emio::format(\"{}\", args).value() == \"123\");\n    CHECK(emio::format(\"{}\", base_args).value() == \"123\");\n    CHECK(emio::format(\"{} {}\", args, base_args).value() == \"123 123\");\n    CHECK(emio::format(\"{}\", emio::make_format_args(\"{}\", emio::make_format_args(\"{}\", args))).value() == \"123\");\n    CHECK(emio::format(\"{}\", emio::make_format_args(\"{}\", 123)).value() == \"123\");\n    CHECK(emio::format(\"{}\", emio::make_format_args(emio::runtime(\"{}\"), 123)).value() == \"123\");\n    CHECK(emio::format(\"{}\", emio::make_format_args(emio::runtime(\"{}\"))) == emio::err::invalid_format);\n    CHECK(emio::format(\"{}\", emio::make_format_args(emio::runtime(\"{\"), 123)) == emio::err::invalid_format);\n\n    CHECK(emio::print(\"{}\", args).has_value());\n    CHECK(emio::print(\"{}\", base_args).has_value());\n    CHECK(emio::print(\"{} {}\", args, base_args).has_value());\n    CHECK(emio::print(\"{}\", emio::make_format_args(\"{}\", emio::make_format_args(\"{}\", args))).has_value());\n    CHECK(emio::print(\"{}\", emio::make_format_args(\"{}\", 123)).has_value());\n    CHECK(emio::print(\"{}\", emio::make_format_args(emio::runtime(\"{}\"), 123)).has_value());\n    CHECK(emio::print(\"{}\", emio::make_format_args(emio::runtime(\"{}\"))) == emio::err::invalid_format);\n    CHECK(emio::print(\"{}\", emio::make_format_args(emio::runtime(\"{\"), 123)) == emio::err::invalid_format);\n\n    CHECK(emio::println(\"{}\", args).has_value());\n    CHECK(emio::println(\"{}\", base_args).has_value());\n    CHECK(emio::println(\"{} {}\", args, base_args).has_value());\n    CHECK(emio::println(\"{}\", emio::make_format_args(\"{}\", emio::make_format_args(\"{}\", args))).has_value());\n    CHECK(emio::println(\"{}\", emio::make_format_args(\"{}\", 123)).has_value());\n    CHECK(emio::println(\"{}\", emio::make_format_args(emio::runtime(\"{}\"), 123)).has_value());\n    CHECK(emio::println(\"{}\", emio::make_format_args(emio::runtime(\"{}\"))) == emio::err::invalid_format);\n    CHECK(emio::println(\"{}\", emio::make_format_args(emio::runtime(\"{\"), 123)) == emio::err::invalid_format);\n\n    STATIC_CHECK_FALSE(emio::format_can_fail_v<int>);\n    STATIC_CHECK_FALSE(emio::format_can_fail_v<double>);\n    STATIC_CHECK_FALSE(emio::format_can_fail_v<std::string>);\n    STATIC_CHECK(emio::format_can_fail_v<emio::format_args>);\n    STATIC_CHECK(emio::format_can_fail_v<decltype(args)>);\n  };\n\n  // Use a separate function so args lives longer.\n  check(emio::make_format_args(\"{}\", 123));\n}\n"
  },
  {
    "path": "test/unit_test/test_format_emio_vs_fmt.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <fmt/format.h>\n\n#include <catch2/catch_test_macros.hpp>\n#include <catch2/generators/catch_generators_adapters.hpp>\n#include <catch2/generators/catch_generators_random.hpp>\n#include <string>\n\nTEST_CASE(\"format double with emio vs fmt\") {\n  static constexpr std::array values = {\n      0.0,\n      1.0,\n      12.0,\n      123.0,\n      1230.0,\n      12300.0,\n      123456789098765.0,\n      1234567890987653.0,\n      9.999999999999999e22,\n      1.2,\n      12.34,\n      123.56,\n      123456789098765.5,\n      1234567890987653.9,\n      0.99999,\n      0.1,\n      0.012,\n      0.00123,\n      0.00000123,\n      std::numeric_limits<double>::min(),\n      std::numeric_limits<double>::max(),\n      std::numeric_limits<double>::lowest(),\n      M_PI,\n  };\n\n  auto test = [](double value) {\n    static constexpr std::array test_formats = {\"\", \"g\", \"e\", \"f\"};\n\n    for (auto precision : {\"\", \".0\", \".4\"}) {\n      for (auto alternate : {\"\", \"#\"}) {\n        for (auto test_format : test_formats) {\n          auto format_string = fmt::format(\"{{:{}{}{}}}\", alternate, precision, test_format);\n          for (double sign : {-1.0, 1.0}) {\n            value *= sign;\n            SECTION(fmt::format(\"emio vs fmt for {} format as {}\", value, format_string)) {\n              auto fmt_result = fmt::format(fmt::runtime(format_string), value);\n              auto emio_result = emio::format(emio::runtime(format_string), value);\n              REQUIRE(emio_result);\n              CHECK(emio_result.value() == fmt_result);\n            }\n          }\n        }\n      }\n    }\n  };\n\n  for (double value : values) {\n    test(value);\n  }\n}\n\nTEST_CASE(\"format double for shortest results in different rounding\") {\n  double d1 = 1234567890987653.25;\n  double d2 = 1234567890987654.25;\n\n  // Same result in Rust. Algorithm seems to always round up on 0.5.\n  CHECK(emio::format(\"{}\", d1) == \"1234567890987653.3\");\n  CHECK(emio::format(\"{}\", d2) == \"1234567890987654.3\");\n\n  // Algorithm seems to always round down on 0.5.\n  CHECK(fmt::format(\"{}\", d1) == \"1234567890987653.2\");\n  CHECK(fmt::format(\"{}\", d2) == \"1234567890987654.2\");\n}\n"
  },
  {
    "path": "test/unit_test/test_format_string.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namespace std::string_view_literals;\nusing namespace std::string_literals;\n\nnamespace {\n\nconstexpr std::string_view format_str{\"abc {} {} {}\"};\nconstexpr emio::format_string<int, char, std::string_view> precompiled_format_str{format_str};\nconstexpr emio::valid_format_string<int, char, std::string_view> precompiled_validated_format_str{\n    precompiled_format_str.as_valid().value()};\nconstexpr std::string_view expected_str{\"abc 42 x hello\"};\n\n}  // namespace\n\nTEST_CASE(\"allowed types for format string\", \"[format_string]\") {\n  // Test strategy:\n  // * Call emio::format, make_format_args and formatted_size with different format string types.\n  // Expected: Constexpr, runtime and precompiled format string work, the results are the same.\n\n  SECTION(\"format\") {\n    std::string str = emio::format(format_str, 42, 'x', \"hello\"sv);\n    CHECK(str == expected_str);\n\n    emio::result<std::string> res = emio::format(emio::runtime(format_str), 42, 'x', \"hello\"sv);\n    CHECK(res == expected_str);\n\n    res = emio::format(precompiled_format_str, 42, 'x', \"hello\"sv);\n    CHECK(res == expected_str);\n\n    str = emio::format(precompiled_validated_format_str, 42, 'x', \"hello\"sv);\n    CHECK(str == expected_str);\n  }\n\n  SECTION(\"make_format_args\") {\n    {\n      emio::format_args&& args = emio::make_format_args(format_str, 42, 'x', \"hello\"sv);\n      static_cast<void>(args);\n    }\n\n    {\n      emio::format_args&& args = emio::make_format_args(emio::runtime(format_str), 42, 'x', \"hello\"sv);\n      static_cast<void>(args);\n    }\n    {\n      emio::format_args&& args = emio::make_format_args(precompiled_format_str, 42, 'x', \"hello\"sv);\n      static_cast<void>(args);\n    }\n  }\n\n  SECTION(\"formatted_size\") {\n    {\n      constexpr size_t res = emio::formatted_size(format_str, 42, 'x', \"hello\"sv);\n      STATIC_CHECK(res == 14UL);\n    }\n\n    {\n      constexpr emio::result<size_t> res = emio::formatted_size(emio::runtime(format_str), 42, 'x', \"hello\"sv);\n      STATIC_CHECK(res == 14UL);\n    }\n    {\n      constexpr emio::result<size_t> res = emio::formatted_size(precompiled_format_str, 42, 'x', \"hello\"sv);\n      STATIC_CHECK(res == 14UL);\n    }\n    {\n      constexpr size_t res = emio::formatted_size(precompiled_validated_format_str, 42, 'x', \"hello\"sv);\n      STATIC_CHECK(res == 14UL);\n    }\n  }\n}\n\nTEST_CASE(\"runtime format_string\", \"[format_string]\") {\n  // Test strategy:\n  // * Construct an emio::runtime from different string types.\n  // Expected: Everything works as expected.\n\n  CHECK(emio::runtime_string{}.get().empty());\n\n  constexpr emio::runtime_string from_char_seq_at_cp{\"12{3\"};\n  STATIC_CHECK(from_char_seq_at_cp.get() == \"12{3\");\n\n  emio::runtime_string from_char_seq{\"12{3\"};\n  CHECK(from_char_seq.get() == \"12{3\");\n\n  emio::runtime_string from_string_view{\"12{3\"sv};\n  CHECK(from_string_view.get() == \"12{3\");\n\n  std::string s{\"12{3\"};\n  emio::runtime_string from_string{s};\n  CHECK(from_string.get() == \"12{3\");\n\n  emio::format_string<int, char, std::string_view> str{emio::runtime_string{\"{\"}};\n  CHECK(str.get() == emio::err::invalid_format);\n  CHECK(str.as_valid() == emio::err::invalid_format);\n\n  emio::format_string<int, char> str2{emio::runtime_string{\"{} {}\"}};\n  CHECK(str2.get() == \"{} {}\");\n  emio::result<emio::valid_format_string<int, char>> valid = str2.as_valid();\n  REQUIRE(valid);\n  CHECK(valid->get() == \"{} {}\");\n\n  emio::result<emio::valid_format_string<int, char>> res = emio::valid_format_string<int, char>::from(\"{}\");\n  CHECK(res == emio::err::invalid_format);\n\n  res = emio::valid_format_string<int, char>::from(\"{} {}\");\n  REQUIRE(res);\n  CHECK(res->get() == \"{} {}\");\n}\n\nTEST_CASE(\"is_plain_str\", \"[format_string]\") {\n  CHECK(emio::format_string<>{\"\"}.is_plain_str());\n  CHECK(emio::format_string<>{\"\"}.empty());\n  CHECK(emio::format_string<>{\"abc 123\"}.is_plain_str());\n  CHECK(emio::format_string<>{emio::runtime(\"abc 123\")}.is_plain_str());\n  CHECK(emio::make_format_args(emio::runtime(\"abc 123\")).is_plain_str());\n\n  CHECK_FALSE(emio::format_string<>{\"abc 123 {{\"}.is_plain_str());\n  CHECK_FALSE(emio::format_string<>{\"abc 123 {{\"}.empty());\n  CHECK_FALSE(emio::format_string<>{emio::runtime(\"abc 123 {{\")}.is_plain_str());\n  CHECK_FALSE(emio::format_string<>{emio::runtime(\"abc 123 {{\")}.empty());\n  CHECK_FALSE(emio::make_format_args(emio::runtime(\"abc 123 {{\")).is_plain_str());\n  CHECK_FALSE(emio::make_format_args(emio::runtime(\"abc 123 {{\")).empty());\n\n  CHECK_FALSE(emio::format_string<>{\"abc }} 123\"}.is_plain_str());\n  CHECK_FALSE(emio::format_string<>{emio::runtime(\"abc }} 123\")}.is_plain_str());\n  CHECK_FALSE(emio::make_format_args(emio::runtime(\"abc }} 123\")).is_plain_str());\n\n  CHECK_FALSE(emio::format_string<>{emio::runtime(\"abc {} 123\")}.is_plain_str());\n  CHECK_FALSE(emio::make_format_args(emio::runtime(\"abc {} 123\")).is_plain_str());\n\n  CHECK_FALSE(emio::make_format_args(emio::runtime(\"\"), 1).is_plain_str());\n  CHECK_FALSE(emio::make_format_args(emio::runtime(\"\"), 1).empty());\n\n  CHECK_FALSE(precompiled_format_str.is_plain_str());\n  CHECK_FALSE(emio::format_string<>{emio::runtime(format_str)}.is_plain_str());\n}\n\nTEST_CASE(\"default constructed format_args\", \"[format_string]\") {\n  const auto check = [](const auto& args) {\n    CHECK(args.is_plain_str());\n    CHECK(args.get_str().value().empty());\n    CHECK(args.get_args().empty());\n    CHECK(args.empty());\n\n    CHECK(emio::vformat(args).value() == \"\");\n    CHECK(emio::format(\"{}\", args).value() == \"\");\n  };\n\n  SECTION(\"default constructed\") {\n    emio::format_args args;\n    check(args);\n  }\n  SECTION(\"empty constructed\") {\n    check(emio::make_format_args(\"\"));\n  }\n}\n"
  },
  {
    "path": "test/unit_test/test_format_to_api.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namespace std::string_view_literals;\n\nTEST_CASE(\"emio::format_to with output iterator\", \"[format_to]\") {\n  // Test strategy:\n  // * Call emio::format_to with all supported output iterator types.\n  // Expected: The return types, values and the format results are correct.\n\n  SECTION(\"raw ptr\") {\n    SECTION(\"compile-time\") {\n      constexpr bool success = [] {\n        std::array<char, 2> arr{};\n\n        emio::result<char*> res = emio::format_to(arr.begin(), \"{}\", 42);\n        return res == arr.end() && arr[0] == '4' && arr[1] == '2';\n      }();\n      STATIC_CHECK(success);\n    }\n    SECTION(\"runtime\") {\n      std::array<char, 2> arr{};\n\n      emio::result<char*> res = emio::format_to(arr.begin(), \"{}\", 42);\n      CHECK(res);\n      CHECK(arr[0] == '4');\n      CHECK(arr[1] == '2');\n    }\n  }\n\n  SECTION(\"iterator\") {\n    std::string s;\n    s.resize(2);\n\n    emio::result<std::string::iterator> res = emio::format_to(s.begin(), \"{}\", 42);\n    REQUIRE(res == s.end());\n    CHECK(s == \"42\");\n  }\n\n  SECTION(\"back_insert_iterator\") {\n    std::string s;\n\n    emio::result<std::back_insert_iterator<std::string>> res = emio::format_to(std::back_inserter(s), \"{}\", 42);\n    REQUIRE(res);\n    CHECK(s == \"42\");\n  }\n}\n\nTEST_CASE(\"emio::format_to with emio::buffer\", \"[format_to]\") {\n  // Test strategy:\n  // * Call emio::format_to with an emio::static_buffer.\n  // Expected: The return type, value and the format result is correct.\n\n  SECTION(\"compile-time\") {\n    SECTION(\"success\") {\n      constexpr bool success = [] {\n        emio::static_buffer<2> buf{};\n        emio::result<void> res = emio::format_to(buf, \"42\");\n        return res && buf.view() == \"42\";\n      }();\n      STATIC_CHECK(success);\n\n      constexpr bool success2 = [] {\n        emio::static_buffer<2> buf{};\n\n        emio::result<void> res = emio::format_to(buf, \"{}\", 42);\n        return res && buf.view() == \"42\";\n      }();\n      STATIC_CHECK(success2);\n    }\n    SECTION(\"eof\") {\n      constexpr bool eof = [] {\n        emio::static_buffer<2> buf{};\n\n        emio::result<void> res = emio::format_to(buf, \"{}\", 420);\n        return res == emio::err::eof;\n      }();\n      STATIC_CHECK(eof);\n    }\n  }\n  SECTION(\"runtime\") {\n    emio::static_buffer<2> buf{};\n\n    SECTION(\"success\") {\n      emio::result<void> res = emio::format_to(buf, \"{}\", 42);\n      REQUIRE(res);\n      CHECK(buf.view() == \"42\");\n    }\n    SECTION(\"eof\") {\n      emio::result<void> res = emio::format_to(buf, \"{}\", 420);\n      REQUIRE(res == emio::err::eof);\n    }\n  }\n}\n\nTEST_CASE(\"emio::format_to with emio::writer\", \"[format_to]\") {\n  // Test strategy:\n  // * Call emio::format_to with an emio::writer.\n  // Expected: The return type, value and the format result is correct.\n\n  emio::static_buffer<2> buf{};\n  emio::writer wrt{buf};\n\n  SECTION(\"success\") {\n    emio::result<void> res = emio::format_to(wrt, \"{}\", 42);\n    REQUIRE(res);\n    CHECK(buf.view() == \"42\");\n  }\n  SECTION(\"eof\") {\n    emio::result<void> res = emio::format_to(wrt, \"{}\", 420);\n    REQUIRE(res == emio::err::eof);\n  }\n}\n\nTEST_CASE(\"emio::vformat_to with output iterator\", \"[format_to]\") {\n  // Test strategy:\n  // * Call emio::vformat_to with all supported output iterator types.\n  // Expected: The return types, values and the format results are correct.\n\n  SECTION(\"raw ptr\") {\n    std::array<char, 2> arr{};\n\n    emio::result<char*> res = emio::vformat_to(arr.begin(), emio::make_format_args(\"{}\", 42));\n    REQUIRE(res == arr.end());\n    CHECK(arr[0] == '4');\n    CHECK(arr[1] == '2');\n  }\n\n  SECTION(\"iterator\") {\n    std::string s;\n    s.resize(2);\n\n    emio::result<std::string::iterator> res = emio::vformat_to(s.begin(), emio::make_format_args(\"{}\", 42));\n    REQUIRE(res == s.end());\n    CHECK(s == \"42\");\n  }\n\n  SECTION(\"back_insert_iterator\") {\n    std::string s;\n\n    emio::result<std::back_insert_iterator<std::string>> res =\n        emio::vformat_to(std::back_inserter(s), emio::make_format_args(\"{}\", 42));\n    REQUIRE(res);\n    CHECK(s == \"42\");\n  }\n}\n\nTEST_CASE(\"emio::vformat_to with emio::buffer\", \"[format_to]\") {\n  // Test strategy:\n  // * Call emio::vformat_to with an emio::static_buffer.\n  // Expected: The return type, value and the format result is correct.\n\n  emio::static_buffer<2> buf{};\n\n  SECTION(\"success\") {\n    emio::result<void> res = emio::vformat_to(buf, emio::make_format_args(\"{}\", 42));\n    REQUIRE(res);\n    CHECK(buf.view() == \"42\");\n  }\n  SECTION(\"eof\") {\n    emio::result<void> res = emio::vformat_to(buf, emio::make_format_args(\"{}\", 420));\n    REQUIRE(res == emio::err::eof);\n  }\n}\n\nTEST_CASE(\"emio::vformat_to with emio::writer\", \"[format_to]\") {\n  // Test strategy:\n  // * Call emio::vformat_to with an emio::writer.\n  // Expected: The return type, value and the format result is correct.\n\n  emio::static_buffer<2> buf{};\n  emio::writer wrt{buf};\n\n  SECTION(\"success\") {\n    emio::result<void> res = emio::vformat_to(wrt, emio::make_format_args(\"{}\", 42));\n    REQUIRE(res);\n    CHECK(buf.view() == \"42\");\n  }\n  SECTION(\"eof\") {\n    emio::result<void> res = emio::vformat_to(wrt, emio::make_format_args(\"{}\", 420));\n    REQUIRE(res == emio::err::eof);\n  }\n}\n\nTEST_CASE(\"emio::format_to with truncating_buffer\", \"[format_to]\") {\n  // Test strategy:\n  // * Write a longer message than the specified limit into a truncating buffer.\n  // Expected: The primary buffer only has a subset of the message and can later be used independently.\n\n  SECTION(\"runtime\") {\n    emio::static_buffer<13> primary_buf;\n    emio::truncating_buffer buf{primary_buf, 10};\n\n    REQUIRE(emio::format_to(buf, \"Hello {}!\", 123456789));\n\n    CHECK(buf.count() == 16);\n    REQUIRE(buf.flush());\n    CHECK(primary_buf.view() == \"Hello 1234\");\n    CHECK(buf.count() == 16);\n\n    REQUIRE(emio::format_to(primary_buf, \"...\"));\n    CHECK(primary_buf.view() == \"Hello 1234...\");\n  }\n  SECTION(\"compile-time\") {\n    constexpr bool success = [] {\n      emio::static_buffer<13> primary_buf;\n      emio::truncating_buffer buf{primary_buf, 1};\n      emio::format_to(buf, \"{}\", 42).value();\n      buf.flush().value();\n      return buf.count() == 2U && primary_buf.view() == \"4\";\n    }();\n    STATIC_CHECK(success);\n  }\n}\n"
  },
  {
    "path": "test/unit_test/test_format_to_n_api.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namespace std::string_view_literals;\n\nTEST_CASE(\"emio::format_to_n with output iterator\", \"[format_to_n]\") {\n  // Test strategy:\n  // * Call emio::format_to_n with all supported output iterator types.\n  // Expected: The return types, values and the format results are correct.\n\n  SECTION(\"raw ptr\") {\n    SECTION(\"compile-time\") {\n      constexpr bool success = [] {\n        std::array<char, 2> arr{};\n\n        emio::result<emio::format_to_n_result<char*>> res = emio::format_to_n(arr.begin(), 1, \"{}\", 421);\n        return res && res->size == 3 && (res->out == arr.begin() + 1) && arr[0] == '4' && arr[1] == '\\0';\n      }();\n      STATIC_CHECK(success);\n    }\n    SECTION(\"runtime\") {\n      std::array<char, 2> arr{};\n\n      emio::result<emio::format_to_n_result<char*>> res = emio::format_to_n(arr.begin(), 1, \"{}\", 421);\n      CHECK(res);\n      CHECK(res->size == 3);\n      CHECK(arr[0] == '4');\n      CHECK(arr[1] == '\\0');\n    }\n  }\n\n  SECTION(\"iterator\") {\n    std::string s;\n    s.resize(2);\n\n    emio::result<emio::format_to_n_result<std::string::iterator>> res = emio::format_to_n(s.begin(), 1, \"{}\", 421);\n    REQUIRE(res);\n    CHECK(res->size == 3);\n    CHECK(res->out == s.begin() + 1);\n    CHECK(s == \"4\\0\"sv);\n  }\n\n  SECTION(\"back_insert_iterator\") {\n    std::string s;\n\n    emio::result<emio::format_to_n_result<std::back_insert_iterator<std::string>>> res =\n        emio::format_to_n(std::back_inserter(s), 1, \"{}\", 421);\n    REQUIRE(res);\n    CHECK(res->size == 3);\n    CHECK(s == \"4\");\n  }\n}\n\nTEST_CASE(\"emio::vformat_to_n with output iterator\", \"[format_to_n]\") {\n  // Test strategy:\n  // * Call emio::vformat_to with all supported output iterator types.\n  // Expected: The return types, values and the format results are correct.\n\n  SECTION(\"raw ptr\") {\n    std::array<char, 2> arr{};\n\n    emio::result<emio::format_to_n_result<char*>> res =\n        emio::vformat_to_n(arr.begin(), 1, emio::make_format_args(\"{}\", 421));\n    REQUIRE(res);\n    CHECK(res->size == 3);\n    CHECK(res->out == arr.begin() + 1);\n    CHECK(arr[0] == '4');\n    CHECK(arr[1] == '\\0');\n  }\n\n  SECTION(\"iterator\") {\n    std::string s;\n    s.resize(2);\n\n    emio::result<emio::format_to_n_result<std::string::iterator>> res =\n        emio::vformat_to_n(s.begin(), 1, emio::make_format_args(\"{}\", 421));\n    REQUIRE(res);\n    CHECK(res->size == 3);\n    CHECK(res->out == s.begin() + 1);\n    CHECK(s == \"4\\0\"sv);\n  }\n\n  SECTION(\"back_insert_iterator\") {\n    std::string s;\n\n    emio::result<emio::format_to_n_result<std::back_insert_iterator<std::string>>> res =\n        emio::vformat_to_n(std::back_inserter(s), 1, emio::make_format_args(\"{}\", 421));\n    REQUIRE(res);\n    CHECK(res->size == 3);\n    CHECK(s == \"4\");\n  }\n}\n\nTEST_CASE(\"emio::vformat_to_n with buffer\", \"[format_to_n]\") {\n  // Test strategy:\n  // * Write a longer message than the specified limit into a buffer.\n  // Expected: The buffer only has a subset of the message and can later be used independently.\n  emio::static_buffer<13> buf;\n\n  emio::result<size_t> count = emio::vformat_to_n(buf, 10, emio::make_format_args(\"Hello {}!\", 123456789));\n\n  CHECK(count == 16U);\n  CHECK(buf.view() == \"Hello 1234\");\n\n  REQUIRE(emio::format_to(buf, \"...\"));\n  CHECK(buf.view() == \"Hello 1234...\");\n}\n\nTEST_CASE(\"emio::format_to_n with buffer\", \"[format_to_n]\") {\n  // Test strategy:\n  // * Write a longer message than the specified limit into a truncating buffer.\n  // Expected: The primary buffer only has a subset of the message and can later be used independently.\n\n  SECTION(\"runtime\") {\n    emio::static_buffer<13> buf;\n\n    const emio::result<size_t> count = emio::format_to_n(buf, 10, \"Hello {}!\", 123456789);\n\n    CHECK(count == 16U);\n    CHECK(buf.view() == \"Hello 1234\");\n\n    REQUIRE(emio::format_to(buf, \"...\"));\n    CHECK(buf.view() == \"Hello 1234...\");\n  }\n  SECTION(\"compile-time\") {\n    constexpr bool success = [] {\n      emio::static_buffer<10> buf{};\n      const emio::result<size_t> count = emio::format_to_n(buf, 1, \"{}\", 42);\n      return count == 2U && buf.view() == \"4\";\n    }();\n    STATIC_CHECK(success);\n  }\n}\n"
  },
  {
    "path": "test/unit_test/test_formatted_size.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namespace std::string_view_literals;\n\nTEST_CASE(\"formatted_size\", \"[formatted_size]\") {\n  // Test strategy:\n  // * Call emio::formatted_size to determine the total number of characters needed.\n  // Expected: The return type, value and the result is correct.\n\n  STATIC_CHECK(emio::formatted_size(\"{}\", 1) == 1U);\n  STATIC_CHECK(emio::formatted_size(\"{} {}\", 1, 45) == 4U);\n  STATIC_CHECK(emio::formatted_size(emio::runtime(\"{} {}\"), 1, 45) == 4U);\n  STATIC_CHECK(emio::formatted_size(emio::runtime(\"{}\"), 1, 45) == emio::err::invalid_format);\n\n  const size_t res = emio::formatted_size(\"{}\", 1);\n  CHECK(res == 1U);\n  CHECK(emio::formatted_size(\"{} {}\", 1, 45) == 4U);\n  CHECK(emio::formatted_size(emio::runtime(\"{} {}\"), 1, 45) == 4U);\n  CHECK(emio::formatted_size(emio::runtime(\"{}\"), 1, 45) == emio::err::invalid_format);\n}\n\nTEST_CASE(\"vformatted_size\", \"[formatted_size]\") {\n  // Test strategy:\n  // * Call emio::vformatted_size to determine the total number of characters needed.\n  // Expected: The return type, value and the result is correct.\n\n  emio::result<size_t> res = emio::vformatted_size(emio::make_format_args(\"{}\", 1));\n  CHECK(res == 1U);\n  CHECK(emio::vformatted_size(emio::make_format_args(\"{} {}\", 1, 45)) == 4U);\n  CHECK(emio::vformatted_size(emio::make_format_args(emio::runtime(\"{}\"), 1, 45)) == emio::err::invalid_format);\n}\n"
  },
  {
    "path": "test/unit_test/test_formatter.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nnamespace {\n\nstruct wrap {\n  int id;\n};\n\n}  // namespace\n\ntemplate <>\nclass emio::formatter<wrap> : public emio::formatter<int> {\n public:\n  constexpr result<void> format(writer& out, const wrap& arg) const noexcept {\n    return formatter<int>::format(out, arg.id);\n  }\n};\n\nTEST_CASE(\"formatter with inherit from emio::formatter\", \"[formatter]\") {\n  // Test strategy:\n  // * Format a custom type with a formatter which inherit from a base emio::formatter.\n  // Expected: The formatting works and the format options from the base emio::formatter can be used.\n\n  SECTION(\"compile-time\") {\n    SECTION(\"simple\") {\n      constexpr bool success = [] {\n        emio::static_buffer<10> buf{};\n\n        static_cast<void>(emio::format_to(buf, \"{}\", wrap{42}).value());\n        return buf.view() == \"42\";\n      }();\n      STATIC_CHECK(success);\n    }\n    SECTION(\"complex\") {\n      constexpr bool success = [] {\n        emio::static_buffer<10> buf{};\n\n        static_cast<void>(emio::format_to(buf, \"{:x<4x}\", wrap{42}).value());\n        return buf.view() == \"2axx\";\n      }();\n      STATIC_CHECK(success);\n    }\n  }\n\n  CHECK(emio::format(\"{}\", wrap{42}) == \"42\");\n  CHECK(emio::format(\"{:x<4x}\", wrap{42}) == \"2axx\");\n}\n\nnamespace {\n\nstruct foo {\n  int id;\n};\n\n}  // namespace\n\ntemplate <>\nclass emio::formatter<foo> {\n public:\n  static constexpr result<void> validate(reader& rdr) noexcept {\n    EMIO_TRY(const char c, rdr.read_char());\n    if (c == '}') {  // Format end.\n      return success;\n    }\n    if (c == '-') {\n      return rdr.read_if_match_char('}');\n    }\n    return emio::err::invalid_format;\n  }\n\n  constexpr result<void> parse(reader& rdr) noexcept {\n    const char c = rdr.read_char().assume_value();\n    if (c == '}') {  // Format end.\n      return success;\n    }\n    if (c == '-') {\n      sign_ = -1;\n    }\n    rdr.pop();\n    return success;\n  }\n\n  constexpr result<void> format(writer& out, const foo& arg) const noexcept {\n    return emio::format_to(out.get_buffer(), \"foo: {}\", sign_ * arg.id);\n  }\n\n private:\n  int sign_{1};\n};\n\nTEST_CASE(\"formatter with constexpr methods\", \"[formatter]\") {\n  // Test strategy:\n  // * Format a custom type with an own formatter providing all necessary constexpr methods.\n  // Expected: The formatting and the format options work.\n\n  SECTION(\"compile-time\") {\n    SECTION(\"simple\") {\n      constexpr bool success = [] {\n        emio::static_buffer<10> buf{};\n\n        static_cast<void>(emio::format_to(buf, \"{}\", foo{42}).value());\n        return buf.view() == \"foo: 42\";\n      }();\n      STATIC_CHECK(success);\n    }\n    SECTION(\"advanced\") {\n      constexpr bool success = [] {\n        emio::static_buffer<10> buf{};\n\n        static_cast<void>(emio::format_to(buf, \"{:-}\", foo{42}).value());\n        return buf.view() == \"foo: -42\";\n      }();\n      STATIC_CHECK(success);\n    }\n    SECTION(\"error\") {\n      constexpr bool success = [] {\n        emio::static_buffer<10> buf{};\n\n        return emio::format_to(buf, emio::runtime(\"{:+}\"), foo{42}) == emio::err::invalid_format;\n      }();\n      STATIC_CHECK(success);\n    }\n  }\n\n  CHECK(emio::format(\"{}\", foo{42}) == \"foo: 42\");\n  CHECK(emio::format(\"{:-}\", foo{42}) == \"foo: -42\");\n  CHECK(emio::format(emio::runtime(\"{:+}\"), foo{42}) == emio::err::invalid_format);\n}\n\nnamespace {\n\nstruct bar {};\n\n}  // namespace\n\ntemplate <>\nclass emio::formatter<bar> {\n public:\n  static result<void> validate(reader& rdr) noexcept {\n    EMIO_TRY(const char c, rdr.read_char());\n    if (c == '}') {  // Format end.\n      return success;\n    }\n    if (c == '-') {\n      return rdr.read_if_match_char('}');\n    }\n    return emio::err::invalid_format;\n  }\n\n  result<void> parse(reader& rdr) noexcept {\n    const char c = rdr.read_char().assume_value();\n    if (c == '}') {  // Format end.\n      return success;\n    }\n    if (c == '-') {\n      upper_case_ = true;\n    }\n    rdr.pop();\n    return success;\n  }\n\n  result<void> format(writer& out, const bar& /*arg*/) const noexcept {\n    if (upper_case_) {\n      return out.write_str(\"BAR\");\n    }\n    return out.write_str(\"bar\");\n  }\n\n private:\n  bool upper_case_{false};\n};\n\nTEST_CASE(\"formatter with non-constexpr methods\", \"[formatter]\") {\n  // Test strategy:\n  // * Format a custom type with an own formatter providing all necessary non-constexpr methods.\n  // Expected: The formatting works.\n\n  CHECK(emio::format(emio::runtime(\"{}\"), bar{}) == \"bar\");\n  CHECK(emio::format(emio::runtime(\"{:-}\"), bar{}) == \"BAR\");\n  CHECK(emio::format(emio::runtime(\"{:+}\"), bar{}) == emio::err::invalid_format);\n}\n\nnamespace {\n\nstruct foobar {\n  int id;\n};\n\n}  // namespace\n\ntemplate <>\nclass emio::formatter<foobar> {\n public:\n  constexpr result<void> parse(reader& rdr) noexcept {\n    EMIO_TRY(const char c, rdr.read_char());\n    if (c == '}') {  // Format end.\n      return success;\n    }\n    if (c == '-') {\n      sign_ = -1;\n      return rdr.read_if_match_char('}');\n    }\n    return emio::err::invalid_format;\n  }\n\n  constexpr result<void> format(writer& out, const foobar& arg) const noexcept {\n    return emio::format_to(out.get_buffer(), \"foobar: {}\", sign_ * arg.id);\n  }\n\n private:\n  int sign_{1};\n};\n\nTEST_CASE(\"formatter with constexpr methods but without validate function\", \"[formatter]\") {\n  // Test strategy:\n  // * Format a custom type with an own formatter providing all necessary constexpr methods.\n  // Expected: The formatting and the format options work.\n\n  SECTION(\"compile-time\") {\n    SECTION(\"simple\") {\n      constexpr bool success = [] {\n        emio::static_buffer<10> buf{};\n\n        static_cast<void>(emio::format_to(buf, \"{}\", foobar{42}).value());\n        return buf.view() == \"foobar: 42\";\n      }();\n      STATIC_CHECK(success);\n    }\n    SECTION(\"advanced\") {\n      constexpr bool success = [] {\n        emio::static_buffer<11> buf{};\n\n        static_cast<void>(emio::format_to(buf, \"{:-}\", foobar{42}).value());\n        return buf.view() == \"foobar: -42\";\n      }();\n      STATIC_CHECK(success);\n    }\n    SECTION(\"error\") {\n      constexpr bool success = [] {\n        emio::static_buffer<10> buf{};\n\n        return emio::format_to(buf, emio::runtime(\"{:+}\"), foobar{42}) == emio::err::invalid_format;\n      }();\n      STATIC_CHECK(success);\n    }\n  }\n\n  CHECK(emio::format(\"{}\", foobar{42}) == \"foobar: 42\");\n  CHECK(emio::format(\"{:-}\", foobar{42}) == \"foobar: -42\");\n  CHECK(emio::format(emio::runtime(\"{:+}\"), foobar{42}) == emio::err::invalid_format);\n}\n\nnamespace {\n\nstruct bazz0 {};\nstruct bazz1 {};\nstruct bazz2 {};\nstruct bazz3 {};\n\n}  // namespace\n\ntemplate <>\nclass emio::formatter<bazz0> {\n public:\n  constexpr result<void> validate(reader& rdr) noexcept;\n};\n\ntemplate <>\nclass emio::formatter<bazz1> {\n public:\n  static void validate();\n};\n\ntemplate <>\nclass emio::formatter<bazz2> {\n public:\n  void validate();\n};\n\ntemplate <>\nclass emio::formatter<bazz3> {\n public:\n  constexpr result<void> validate(reader& rdr) noexcept;\n};\n\nTEST_CASE(\"detail::has_validate_function_v checks\", \"[formatter]\") {\n  STATIC_CHECK(emio::detail::format::has_validate_function_v<wrap>);\n  STATIC_CHECK(emio::detail::format::has_any_validate_function_v<wrap>);\n  STATIC_CHECK(emio::detail::format::has_validate_function_v<foo>);\n  STATIC_CHECK(emio::detail::format::has_any_validate_function_v<foo>);\n  STATIC_CHECK(emio::detail::format::has_validate_function_v<bar>);\n  STATIC_CHECK(emio::detail::format::has_any_validate_function_v<bar>);\n  STATIC_CHECK(!emio::detail::format::has_validate_function_v<foobar>);\n  STATIC_CHECK(!emio::detail::format::has_any_validate_function_v<foobar>);\n\n  STATIC_CHECK(!emio::detail::format::has_validate_function_v<bazz0>);\n  STATIC_CHECK(emio::detail::format::has_any_validate_function_v<bazz0>);\n\n  STATIC_CHECK(!emio::detail::format::has_validate_function_v<bazz1>);\n  STATIC_CHECK(emio::detail::format::has_any_validate_function_v<bazz1>);\n\n  STATIC_CHECK(!emio::detail::format::has_validate_function_v<bazz2>);\n  STATIC_CHECK(emio::detail::format::has_any_validate_function_v<bazz2>);\n\n  STATIC_CHECK(!emio::detail::format::has_validate_function_v<bazz3>);\n  STATIC_CHECK(emio::detail::format::has_any_validate_function_v<bazz3>);\n}\n\nnamespace {\nstruct f1 {};\nstruct f2 {};\nstruct f3 {};\n}  // namespace\n\ntemplate <>\nclass emio::formatter<f1> {\n public:\n};\n\ntemplate <>\nclass emio::formatter<f2> {\n public:\n  static constexpr bool format_can_fail = false;\n};\n\ntemplate <>\nclass emio::formatter<f3> {\n public:\n  static constexpr bool format_can_fail = true;\n};\n\nTEST_CASE(\"emio::format_can_fail checks\", \"[formatter]\") {\n  STATIC_CHECK_FALSE(emio::format_can_fail_v<f1>);\n  STATIC_CHECK_FALSE(emio::format_can_fail_v<f2>);\n  STATIC_CHECK(emio::format_can_fail_v<f3>);\n}\n"
  },
  {
    "path": "test/unit_test/test_iterator.cpp",
    "content": "// Unit under test.\n#include <emio/iterator.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <string>\n\nTEST_CASE(\"truncating_iterator\") {\n  // Test strategy:\n  // * Construct a truncating iterator from different types of std::string iterators.\n  // * Copy a bigger string into the limited truncating iterator.\n  // Expected: Only a part of the bigger string has been copied into the std::string.\n\n  std::string str;\n  constexpr size_t max_length = 10;\n  constexpr std::string_view full_message = \"acbx40!lqcl.qxon0241\";\n  STATIC_REQUIRE(full_message.size() == 20);\n\n  SECTION(\"raw ptr\") {\n    str.resize(max_length);\n\n    auto* it = str.data();\n    emio::truncating_iterator ti{it, max_length};\n\n    for (char c : full_message) {\n      *ti++ = c;\n    }\n\n    CHECK(ti.count() == 20);\n    CHECK(std::distance(ti.out(), &*str.end()) == 0);\n    CHECK(str.size() == 10);\n    CHECK(str == full_message.substr(0, max_length));\n\n    // Possible API calls.\n    char* out = ti.out();\n    static_cast<void>(out);\n    ti++;\n    ++ti;\n    *ti = '#';\n  }\n\n  SECTION(\"iterator\") {\n    str.resize(10);\n\n    auto it = str.begin();\n    emio::truncating_iterator ti{it, max_length};\n\n    for (char c : full_message) {\n      *ti++ = c;\n    }\n\n    CHECK(ti.count() == 20);\n    CHECK(std::distance(ti.out(), str.end()) == 0);\n    CHECK(str.size() == 10);\n    CHECK(str == full_message.substr(0, max_length));\n\n    // Possible API calls.\n    std::string::iterator out = ti.out();\n    static_cast<void>(out);\n    ti++;\n    ++ti;\n    *ti = '#';\n  }\n\n  SECTION(\"back_insert_iterator\") {\n    auto it = std::back_inserter(str);\n    emio::truncating_iterator ti{it, max_length};\n\n    CHECK(str.empty());\n\n    for (char c : full_message) {\n      *ti++ = c;\n    }\n\n    CHECK(ti.count() == 20);\n    CHECK(str.size() == 10);\n    CHECK(str == full_message.substr(0, max_length));\n\n    // Possible API calls.\n    std::back_insert_iterator<std::string> out = ti.out();\n    static_cast<void>(out);\n    ti++;\n    ++ti;\n    *ti = '#';\n    ti = '#';\n  }\n}\n"
  },
  {
    "path": "test/unit_test/test_print.cpp",
    "content": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nTEST_CASE(\"print/println\") {\n  std::FILE* no_file{};\n\n  emio::print(\"hello {}\", \"world\");\n  CHECK(emio::print(emio::runtime(\"hello {}\"), \"world\"));\n  CHECK(emio::print(stderr, \"hello {}\", \"world\"));\n  CHECK(emio::print(stderr, emio::runtime(\"hello {}\"), \"world\"));\n  CHECK(emio::print(no_file, emio::runtime(\"hello {}\"), \"world\") == emio::err::invalid_data);\n\n  emio::println(\"hello {}\", \"world\");\n  CHECK(emio::println(emio::runtime(\"hello {}\"), \"world\"));\n  CHECK(emio::println(stderr, \"hello {}\", \"world\"));\n  CHECK(emio::println(stderr, emio::runtime(\"hello {}\"), \"world\"));\n  CHECK(emio::println(no_file, emio::runtime(\"hello {}\"), \"world\") == emio::err::invalid_data);\n}\n\nTEST_CASE(\"print/println to temporary file\") {\n  // Open a temporary file.\n  std::FILE* tmpf = std::tmpfile();\n  REQUIRE(tmpf);\n\n  // Write into.\n  CHECK(emio::println(tmpf, \"hello {}\", \"world\"));\n  CHECK(emio::println(tmpf, \"abc\"));\n\n  // Read out again.\n  std::rewind(tmpf);\n  std::array<char, 13> buf{};\n  CHECK(std::fgets(buf.data(), buf.size(), tmpf) != nullptr);\n  CHECK(std::string_view{buf.data(), 12} == \"hello world\\n\");\n  CHECK(std::fgets(buf.data(), buf.size(), tmpf) != nullptr);\n  CHECK(std::string_view{buf.data(), 4} == \"abc\\n\");\n}\n"
  },
  {
    "path": "test/unit_test/test_ranges.cpp",
    "content": "// Formatting library for C++ - the core API\n//\n// Copyright (c) 2012 - present, Victor Zverovich\n// All rights reserved.\n//\n// For the license information refer to format.h.\n//\n// Copyright (c) 2018 - present, Remotion (Igor Schulz)\n// All Rights Reserved\n// {fmt} support for ranges, containers and types tuple interface.\n\n// Unit under test.\n#include <emio/ranges.hpp>\n\n// Other includes.\n#include <fmt/ranges.h>\n\n#include <catch2/catch_test_macros.hpp>\n#include <emio/format.hpp>\n#include <list>\n#include <map>\n#include <queue>\n#include <set>\n#include <stack>\n#include <string>\n#include <vector>\n\n// Test cases from fmt/test/a-test.cc - 9.1.0\n\nTEST_CASE(\"format_array\", \"[ranges]\") {\n  const int arr[] = {1, 2, 3, 5, 7, 11};\n  CHECK(emio::format(\"{}\", arr) == \"[1, 2, 3, 5, 7, 11]\");\n}\n\nTEST_CASE(\"ranges invalid_format or validate tests\", \"[ranges]\") {\n  const int arr[] = {1, 2};\n  CHECK(emio::format(emio::runtime(\"{}\"), arr) == \"[1, 2]\");\n  CHECK(emio::format(emio::runtime(\"{:x\"), arr) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{:n\"), arr) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{::i}\"), arr) == emio::err::invalid_format);\n\n  emio::static_buffer<1> buf{};\n  CHECK(emio::format_to(buf, \"{}\", arr) == emio::err::eof);\n  CHECK(emio::format_to(buf, \"x{}\", arr) == emio::err::eof);\n}\n\nTEST_CASE(\"format_2d_array\", \"[ranges]\") {\n  int arr[][2] = {{1, 2}, {3, 5}, {7, 11}};\n  CHECK(emio::format(\"{}\", arr) == \"[[1, 2], [3, 5], [7, 11]]\");\n}\n\nTEST_CASE(\"format_array_of_literals\", \"[ranges]\") {\n  const char* arr[] = {\"1234\", \"abcd\"};\n  CHECK(emio::format(\"{}\", arr) == \"[\\\"1234\\\", \\\"abcd\\\"]\");\n  CHECK(emio::format(\"{:n}\", arr) == \"\\\"1234\\\", \\\"abcd\\\"\");\n  CHECK(emio::format(\"{:n:}\", arr) == \"1234, abcd\");\n}\n\nTEST_CASE(\"format_vector\", \"[ranges]\") {\n  auto v = std::vector<int>{1, 2, 3, 5, 7, 11};\n  CHECK(emio::format(\"{}\", v) == \"[1, 2, 3, 5, 7, 11]\");\n  CHECK(emio::format(\"{::#x}\", v) == \"[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]\");\n  CHECK(emio::format(\"{:n:#x}\", v) == \"0x1, 0x2, 0x3, 0x5, 0x7, 0xb\");\n}\n\nTEST_CASE(\"format_vector2\", \"[ranges]\") {\n  auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}};\n  CHECK(emio::format(\"{}\", v) == \"[[1, 2], [3, 5], [7, 11]]\");\n  CHECK(emio::format(\"{:::#x}\", v) == \"[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]\");\n  CHECK(emio::format(\"{:n:n:#x}\", v) == \"0x1, 0x2, 0x3, 0x5, 0x7, 0xb\");\n}\n\nTEST_CASE(\"format_map\", \"[ranges]\") {\n  auto m = std::map<std::string, int>{{\"one\", 1}, {\"two\", 2}};\n  CHECK(emio::format(\"{}\", m) == \"{\\\"one\\\": 1, \\\"two\\\": 2}\");\n  CHECK(emio::format(\"{:n}\", m) == \"\\\"one\\\": 1, \\\"two\\\": 2\");\n  CHECK(emio::format(emio::runtime(\"{:::}\"), m).value() == \"{one: 1, two: 2}\");\n  CHECK(emio::format(emio::runtime(\"{:::^7}\"), m).value() == \"{  one  :    1   ,   two  :    2   }\");\n}\n\nTEST_CASE(\"format_set\", \"[ranges]\") {\n  CHECK(emio::format(\"{}\", std::set<std::string>{\"one\", \"two\"}) == \"{\\\"one\\\", \\\"two\\\"}\");\n}\n\nnamespace adl {\nstruct box {\n  int value;\n};\n\nauto begin(const box& b) -> const int* {\n  return &b.value;\n}\n\nauto end(const box& b) -> const int* {\n  return &b.value + 1;\n}\n}  // namespace adl\n\nTEST_CASE(\"format_adl_begin_end\", \"[ranges]\") {\n  auto b = adl::box{42};\n  CHECK(emio::format(\"{}\", b) == \"[42]\");\n}\n\nTEST_CASE(\"format_pair\", \"[ranges]\") {\n  auto p = std::pair<int, float>(42, 1.5f);\n  CHECK(emio::format(\"{}\", p) == \"(42, 1.5)\");\n  CHECK(emio::format(\"{:n}\", p) == \"42, 1.5\");\n  CHECK(emio::format(\"{::<5}\", p) == \"(42   , 1.5  )\");\n}\n\nTEST_CASE(\"tuple_like invalid_format or validate tests\", \"[ranges]\") {\n  auto p = std::pair<int, float>(42, 1.5f);\n  CHECK(emio::format(emio::runtime(\"{}\"), p) == \"(42, 1.5)\");\n  CHECK(emio::format(emio::runtime(\"{:x\"), p) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{:n\"), p) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{::i}\"), p) == emio::err::invalid_format);\n  CHECK(emio::format(emio::runtime(\"{::}\"), std::tuple<>()) == emio::err::invalid_format);\n\n  emio::static_buffer<1> buf{};\n  CHECK(emio::format_to(buf, \"{}\", p) == emio::err::eof);\n  CHECK(emio::format_to(buf, \"x{}\", p) == emio::err::eof);\n}\n\nstruct unformattable {};\n\nTEST_CASE(\"format_tuple\", \"[ranges]\") {\n  auto t = std::tuple<int, float, std::string, char>(42, 1.5f, \"this is tuple\", 'i');\n  CHECK(emio::format(\"{}\", t) == \"(42, 1.5, \\\"this is tuple\\\", 'i')\");\n  CHECK(emio::format(\"{}\", std::tuple<>()) == \"()\");\n  CHECK(emio::format(\"{:n}\", std::tuple<>()) == \"\");\n\n  STATIC_CHECK(emio::is_formattable_v<std::tuple<>>);\n  STATIC_CHECK(!emio::is_formattable_v<unformattable>);\n  STATIC_CHECK(!emio::is_formattable_v<std::tuple<unformattable>>);\n  STATIC_CHECK(!emio::is_formattable_v<std::tuple<unformattable, int>>);\n  STATIC_CHECK(!emio::is_formattable_v<std::tuple<int, unformattable>>);\n  STATIC_CHECK(!emio::is_formattable_v<std::tuple<unformattable, unformattable>>);\n  STATIC_CHECK(emio::is_formattable_v<std::tuple<int, float>>);\n}\n\nstruct tuple_like {\n  int i;\n  std::string str;\n\n  template <size_t N>\n    requires(N == 0)\n  int get() const noexcept {\n    return i;\n  }\n  template <size_t N>\n    requires(N == 1)\n  std::string_view get() const noexcept {\n    return str;\n  }\n};\n\ntemplate <size_t N>\nauto get(const tuple_like& t) noexcept -> decltype(t.get<N>()) {\n  return t.get<N>();\n}\n\nnamespace std {\ntemplate <>\nstruct tuple_size<tuple_like> : std::integral_constant<size_t, 2> {};\n\ntemplate <size_t N>\nstruct tuple_element<N, tuple_like> {\n  using type = decltype(std::declval<tuple_like>().get<N>());\n};\n}  // namespace std\n\nTEST_CASE(\"format_struct\", \"[ranges]\") {\n  auto t = tuple_like{42, \"foo\"};\n  CHECK(emio::format(\"{}\", t) == \"(42, \\\"foo\\\")\");\n}\n\nTEST_CASE(\"format_to\", \"[ranges]\") {\n  char buf[10];\n  auto end = emio::format_to(buf, \"{}\", std::vector<int>{1, 2, 3}).value();\n  *end = '\\0';\n  CHECK(std::string_view{buf} == \"[1, 2, 3]\");\n}\n\n// struct path_like {\n//   const path_like* begin() const;\n//   const path_like* end() const;\n//\n//   operator std::string() const;\n// };\n//\n// TEST_CASE(\"path_like\", \"[ranges]\") {\n//   EXPECT_FALSE((fmt::is_range<path_like, char>::value));\n// }\n//\n//// A range that provides non-const only begin()/end() to test fmt::join handles\n//// that.\n////\n//// Some ranges (e.g. those produced by range-v3's views::filter()) can cache\n//// information during iteration so they only provide non-const begin()/end().\n// template <typename T>\n// class non_const_only_range {\n//  private:\n//   std::vector<T> vec;\n//\n//  public:\n//   using const_iterator = typename ::std::vector<T>::const_iterator;\n//\n//   template <typename... Args>\n//   explicit non_const_only_range(Args&&... args) : vec(std::forward<Args>(args)...) {}\n//\n//   const_iterator begin() {\n//     return vec.begin();\n//   }\n//   const_iterator end() {\n//     return vec.end();\n//   }\n// };\n//\n// template <typename T>\n// class noncopyable_range {\n//  private:\n//   std::vector<T> vec;\n//\n//  public:\n//   using const_iterator = typename ::std::vector<T>::const_iterator;\n//\n//   template <typename... Args>\n//   explicit noncopyable_range(Args&&... args) : vec(std::forward<Args>(args)...) {}\n//\n//   noncopyable_range(noncopyable_range const&) = delete;\n//   noncopyable_range(noncopyable_range&) = delete;\n//\n//   const_iterator begin() const {\n//     return vec.begin();\n//   }\n//   const_iterator end() const {\n//     return vec.end();\n//   }\n// };\n//\n// TEST_CASE(\"range\", \"[ranges]\") {\n//   noncopyable_range<int> w(3u, 0);\n//   CHECK(emio::format(\"{}\", w) == \"[0, 0, 0]\");\n//   CHECK(emio::format(\"{}\", noncopyable_range<int>(3u, 0)) == \"[0, 0, 0]\");\n//\n//   non_const_only_range<int> x(3u, 0);\n//   CHECK(emio::format(\"{}\", x) == \"[0, 0, 0]\");\n//   CHECK(emio::format(\"{}\", non_const_only_range<int>(3u, 0)) == \"[0, 0, 0]\");\n//\n//   auto y = std::vector<int>(3u, 0);\n//   CHECK(emio::format(\"{}\", y) == \"[0, 0, 0]\");\n//   CHECK(emio::format(\"{}\", std::vector<int>(3u, 0)) == \"[0, 0, 0]\");\n//\n//   const auto z = std::vector<int>(3u, 0);\n//   CHECK(emio::format(\"{}\", z) == \"[0, 0, 0]\");\n// }\n//\n// enum test_enum { foo };\n// auto format_as(test_enum e) -> int {\n//   return e;\n// }\n//\n// TEST_CASE(\"enum_range\", \"[ranges]\") {\n//   auto v = std::vector<test_enum>{test_enum::foo};\n//   CHECK(emio::format(\"{}\", v) == \"[0]\");\n// }\n//\n// TEST_CASE(\"unformattable_range\", \"[ranges]\") {\n//   STATIC_CHECK((!fmt::has_formatter<std::vector<unformattable>, fmt::format_context>::value));\n// }\n//\n// #ifdef FMT_RANGES_TEST_ENABLE_JOIN\n// TEST_CASE(\"join_tuple\", \"[ranges]\") {\n//   // Value tuple args.\n//   auto t1 = std::tuple<char, int, float>('a', 1, 2.0f);\n//   CHECK(emio::format(\"({})\", fmt::join(t1, \", \")) == \"(a, 1, 2)\");\n//\n//   // Testing lvalue tuple args.\n//   int x = 4;\n//   auto t2 = std::tuple<char, int&>('b', x);\n//   CHECK(emio::format(\"{}\", fmt::join(t2, \" + \")) == \"b + 4\");\n//\n//   // Empty tuple.\n//   auto t3 = std::tuple<>();\n//   CHECK(emio::format(\"{}\", fmt::join(t3, \"|\")) == \"\");\n//\n//   // Single element tuple.\n//   auto t4 = std::tuple<float>(4.0f);\n//   CHECK(emio::format(\"{}\", fmt::join(t4, \"/\")) == \"4\");\n//\n// #  if FMT_TUPLE_JOIN_SPECIFIERS\n//   // Specs applied to each element.\n//   auto t5 = std::tuple<int, int, long>(-3, 100, 1);\n//   CHECK(emio::format(\"{:+03}\", fmt::join(t5, \", \")) == \"-03, +100, +01\");\n//\n//   auto t6 = std::tuple<float, double, long double>(3, 3.14, 3.1415);\n//   CHECK(emio::format(\"{:5.5f}\", fmt::join(t6, \", \")) == \"3.00000, 3.14000, 3.14150\");\n//\n//   // Testing lvalue tuple args.\n//   int y = -1;\n//   auto t7 = std::tuple<int, int&, const int&>(3, y, y);\n//   CHECK(emio::format(\"{:03}\", fmt::join(t7, \", \")) == \"003, -01, -01\");\n// #  endif\n// }\n//\n// TEST_CASE(\"join_initializer_list\", \"[ranges]\") {\n//   CHECK(emio::format(\"{}\", fmt::join({1, 2, 3}, \", \")) == \"1, 2, 3\");\n//   CHECK(emio::format(\"{}\", fmt::join({\"fmt\", \"rocks\", \"!\"}, \" \")) == \"fmt rocks !\");\n// }\n//\n// struct zstring_sentinel {};\n//\n// bool operator==(const char* p, zstring_sentinel) {\n//   return *p == '\\0';\n// }\n// bool operator!=(const char* p, zstring_sentinel) {\n//   return *p != '\\0';\n// }\n//\n// struct zstring {\n//   const char* p;\n//   const char* begin() const {\n//     return p;\n//   }\n//   zstring_sentinel end() const {\n//     return {};\n//   }\n// };\n//\n// #  ifdef __cpp_lib_ranges\n// struct cpp20_only_range {\n//   struct iterator {\n//     int val = 0;\n//\n//     using value_type = int;\n//     using difference_type = std::ptrdiff_t;\n//     using iterator_concept = std::input_iterator_tag;\n//\n//     iterator() = default;\n//     iterator(int i) : val(i) {}\n//     int operator*() const {\n//       return val;\n//     }\n//     iterator& operator++() {\n//       ++val;\n//       return *this;\n//     }\n//     void operator++(int) {\n//       ++*this;\n//     }\n//     bool operator==(const iterator& rhs) const {\n//       return val == rhs.val;\n//     }\n//   };\n//\n//   int lo;\n//   int hi;\n//\n//   iterator begin() const {\n//     return iterator(lo);\n//   }\n//   iterator end() const {\n//     return iterator(hi);\n//   }\n// };\n//\n// static_assert(std::input_iterator<cpp20_only_range::iterator>);\n// #  endif\n//\n// TEST_CASE(\"join_sentinel\", \"[ranges]\") {\n//   auto hello = zstring{\"hello\"};\n//   CHECK(emio::format(\"{}\", hello) == \"['h', 'e', 'l', 'l', 'o']\");\n//   CHECK(emio::format(\"{::}\", hello) == \"[h, e, l, l, o]\");\n//   CHECK(emio::format(\"{}\", fmt::join(hello, \"_\")) == \"h_e_l_l_o\");\n// }\n//\n// TEST_CASE(\"join_range\", \"[ranges]\") {\n//   noncopyable_range<int> w(3u, 0);\n//   CHECK(emio::format(\"{}\", fmt::join(w, \",\")) == \"0,0,0\");\n//   CHECK(emio::format(\"{}\", fmt::join(noncopyable_range<int>(3u, 0) == \",\")), \"0,0,0\");\n//\n//   non_const_only_range<int> x(3u, 0);\n//   CHECK(emio::format(\"{}\", fmt::join(x, \",\")) == \"0,0,0\");\n//   CHECK(emio::format(\"{}\", fmt::join(non_const_only_range<int>(3u, 0) == \",\")), \"0,0,0\");\n//\n//   auto y = std::vector<int>(3u, 0);\n//   CHECK(emio::format(\"{}\", fmt::join(y, \",\")) == \"0,0,0\");\n//   CHECK(emio::format(\"{}\", fmt::join(std::vector<int>(3u, 0) == \",\")), \"0,0,0\");\n//\n//   const auto z = std::vector<int>(3u, 0);\n//   CHECK(emio::format(\"{}\", fmt::join(z, \",\")) == \"0,0,0\");\n//\n// #  ifdef __cpp_lib_ranges\n//   CHECK(emio::format(\"{}\", cpp20_only_range{.lo = 0, .hi = 5}) == \"[0, 1, 2, 3, 4]\");\n//   CHECK(emio::format(\"{}\", fmt::join(cpp20_only_range{.lo = 0, .hi = 5}, \",\")) == \"0,1,2,3,4\");\n// #  endif\n// }\n// #endif  // FMT_RANGES_TEST_ENABLE_JOIN\n\nTEST_CASE(\"escape_string\", \"[ranges]\") {\n  using vec = std::vector<std::string>;\n  CHECK(emio::format(\"{}\", vec{\"\\n\\r\\t\\\"\\\\\"}) == \"[\\\"\\\\n\\\\r\\\\t\\\\\\\"\\\\\\\\\\\"]\");\n  CHECK(emio::format(\"{}\", vec{\"\\x07\"}) == \"[\\\"\\\\x07\\\"]\");\n  CHECK(emio::format(\"{}\", vec{\"\\x7f\"}) == \"[\\\"\\\\x7f\\\"]\");\n  CHECK(emio::format(\"{}\", vec{\"n\\xcc\\x83\"}) == \"[\\\"n\\\\xcc\\\\x83\\\"]\");\n\n  //   if (fmt::detail::is_utf8()) {\n  //     CHECK(emio::format(\"{}\", vec{\"\\xcd\\xb8\"}) == \"[\\\"\\\\u0378\\\"]\");\n  //     // Unassigned Unicode code points.\n  //     CHECK(emio::format(\"{}\", vec{\"\\xf0\\xaa\\x9b\\x9e\"}) == \"[\\\"\\\\U0002a6de\\\"]\");\n  //     // Broken utf-8.\n  //     CHECK(emio::format(\"{}\", vec{\"\\xf4\\x8f\\xbf\\xc0\"}) == \"[\\\"\\\\xf4\\\\x8f\\\\xbf\\\\xc0\\\"]\");\n  //     CHECK(emio::format(\"{}\", vec{\"\\xf0\\x28\"}) == \"[\\\"\\\\xf0(\\\"]\");\n  //     CHECK(emio::format(\"{}\", vec{\"\\xe1\\x28\"}) == \"[\\\"\\\\xe1(\\\"]\");\n  //     CHECK(emio::format(\"{}\", vec{std::string(\"\\xf0\\x28\\0\\0anything\", 12)}) == \"[\\\"\\\\xf0(\\\\x00\\\\x00anything\\\"]\");\n  //\n  //     // Correct utf-8.\n  //     CHECK(emio::format(\"{}\", vec{\"понедельник\"}) == \"[\\\"понедельник\\\"]\");\n  //   }\n}\n\n// template <typename R>\n// struct fmt_ref_view {\n//   R* r;\n//\n//   auto begin() const -> decltype(r->begin()) {\n//     return r->begin();\n//   }\n//   auto end() const -> decltype(r->end()) {\n//     return r->end();\n//   }\n// };\n//\n// TEST_CASE(\"range_of_range_of_mixed_const\", \"[ranges]\") {\n//   std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5}};\n//   CHECK(emio::format(\"{}\", v) == \"[[1, 2, 3], [4, 5]]\");\n//\n//   fmt_ref_view<decltype(v)> r{&v};\n//   CHECK(emio::format(\"{}\", r) == \"[[1, 2, 3], [4, 5]]\");\n// }\n//\n// TEST_CASE(\"vector_char\", \"[ranges]\") {\n//   CHECK(emio::format(\"{}\", std::vector<char>{'a', 'b'}) == \"['a', 'b']\");\n// }\n"
  },
  {
    "path": "test/unit_test/test_reader.cpp",
    "content": "// Unit under test.\n#include <emio/reader.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <catch2/generators/catch_generators.hpp>\n\n#include \"integer_ranges.hpp\"\n\nusing namespace std::string_view_literals;\n\nTEST_CASE(\"reader\", \"[reader]\") {\n  // Test strategy:\n  // * Call all trivial read methods.\n  // Expected: The methods work as expected.\n\n  constexpr std::string_view initial_str{\"abc ghi def\"};\n\n  emio::reader reader{initial_str};\n\n  CHECK(reader.pos() == 0);\n  CHECK(reader.cnt_remaining() == 11);\n  CHECK(reader.view_remaining() == initial_str);\n  reader.unpop();\n  CHECK(reader.view_remaining() == initial_str);\n\n  CHECK(reader.peek() == 'a');\n  reader.pop();\n  CHECK(reader.peek() == 'b');\n  reader.pop(3);\n  CHECK(reader.pos() == 4);\n\n  CHECK(reader.cnt_remaining() == 7);\n  CHECK(reader.view_remaining() == \"ghi def\");\n\n  CHECK(reader.read_if_match_char('h') == emio::err::invalid_data);\n  CHECK(reader.read_if_match_char('g') == 'g');\n  CHECK(reader.read_if_match_str(\"hi!\") == emio::err::invalid_data);\n  CHECK(reader.read_if_match_str(\"hi \") == \"hi \");\n\n  CHECK(reader.cnt_remaining() == 3);\n  CHECK(reader.view_remaining() == \"def\");\n\n  CHECK(reader.read_char() == 'd');\n  CHECK(reader.read_n_chars(3) == emio::err::eof);\n  reader.unpop();\n  CHECK(reader.read_n_chars(3) == \"def\");\n  CHECK(reader.peek() == emio::err::eof);\n  CHECK(reader.read_char() == emio::err::eof);\n  CHECK(reader.cnt_remaining() == 0);\n  reader.unpop();\n\n  CHECK(reader.cnt_remaining() == 1);\n  CHECK_FALSE(reader.eof());\n  CHECK(reader.peek() == 'f');\n  CHECK(reader.read_char() == 'f');\n  CHECK(reader.eof());\n  CHECK(reader.cnt_remaining() == 0);\n  CHECK(reader.read_remaining().empty());\n  reader.unpop();\n\n  CHECK(reader.cnt_remaining() == 1);\n  CHECK(reader.read_remaining() == \"f\");\n\n  CHECK(reader.read_if_match_char('e') == emio::err::eof);\n  CHECK(reader.read_if_match_str(\"end\") == emio::err::eof);\n\n  reader.unpop(500);\n  CHECK(reader.view_remaining() == initial_str);\n}\n\nTEST_CASE(\"reader::constructor\", \"[reader]\") {\n  // Test strategy:\n  // * Construct the reader from different types of strings.\n  // Expected: The deduction guide works and all behave the same.\n\n  emio::reader s1{\"abc\"};\n  std::string str{\"abc\"};\n  emio::reader s2{str};\n  emio::reader s3{\"abc\"sv};\n\n  STATIC_CHECK(std::is_same_v<decltype(s1), decltype(s2)>);\n  STATIC_CHECK(std::is_same_v<decltype(s2), decltype(s3)>);\n  STATIC_CHECK(std::is_same_v<decltype(s3), emio::reader>);\n\n  CHECK(s1.read_remaining() == \"abc\");\n  CHECK(s2.read_remaining() == \"abc\");\n  CHECK(s3.read_remaining() == \"abc\");\n}\n\nTEST_CASE(\"reader::parse_int\", \"[reader]\") {\n  // Test strategy:\n  // * Call parse_int with different input, integer types and bases.\n  // Expected: The method works as expected.\n\n  SECTION(\"min/max cases\") {\n    const auto range_check = []<typename T>(std::type_identity<T> /*type*/, const auto& lower_input,\n                                            const auto& upper_input) {\n      CHECK(emio::reader{std::get<0>(lower_input)}.parse_int<T>() == std::numeric_limits<T>::min() + 1);\n      CHECK(emio::reader{std::get<1>(lower_input)}.parse_int<T>() == std::numeric_limits<T>::min());\n      CHECK(emio::reader{std::get<2>(lower_input)}.parse_int<T>() == emio::err::out_of_range);\n      CHECK(emio::reader{std::get<3>(lower_input)}.parse_int<T>() == emio::err::out_of_range);\n\n      CHECK(emio::reader{std::get<0>(upper_input)}.parse_int<T>() == std::numeric_limits<T>::max() - 1);\n      CHECK(emio::reader{std::get<1>(upper_input)}.parse_int<T>() == std::numeric_limits<T>::max());\n      CHECK(emio::reader{std::get<2>(upper_input)}.parse_int<T>() == emio::err::out_of_range);\n      CHECK(emio::reader{std::get<3>(upper_input)}.parse_int<T>() == emio::err::out_of_range);\n    };\n    emio::test::apply_integer_ranges(range_check);\n  }\n\n  SECTION(\"sign is correctly parsed\") {\n    SECTION(\"minus\") {\n      CHECK(emio::reader{\"-\"}.parse_int<int>() == emio::err::eof);\n      CHECK(emio::reader{\"-\"}.parse_int<unsigned>() == emio::err::eof);\n      CHECK(emio::reader{\"- \"}.parse_int<int>() == emio::err::invalid_data);\n      CHECK(emio::reader{\"- \"}.parse_int<unsigned>() == emio::err::invalid_data);\n      CHECK(emio::reader{\"-8\"}.parse_int<int>(8) == emio::err::invalid_data);\n      CHECK(emio::reader{\"-8\"}.parse_int<unsigned>(8) == emio::err::invalid_data);\n      CHECK(emio::reader{\"-7\"}.parse_int<int>(8) == -7);\n      CHECK(emio::reader{\"-7\"}.parse_int<unsigned>(8) == emio::err::out_of_range);\n    }\n    SECTION(\"plus\") {\n      CHECK(emio::reader{\"+\"}.parse_int<int>() == emio::err::eof);\n      CHECK(emio::reader{\"+\"}.parse_int<unsigned>() == emio::err::eof);\n      CHECK(emio::reader{\"+ \"}.parse_int<int>() == emio::err::invalid_data);\n      CHECK(emio::reader{\"+ \"}.parse_int<unsigned>() == emio::err::invalid_data);\n      CHECK(emio::reader{\"+8\"}.parse_int<int>(8) == emio::err::invalid_data);\n      CHECK(emio::reader{\"+8\"}.parse_int<unsigned>(8) == emio::err::invalid_data);\n      CHECK(emio::reader{\"+7\"}.parse_int<int>(8) == 7);\n      CHECK(emio::reader{\"+7\"}.parse_int<unsigned>(8) == 7U);\n    }\n    SECTION(\"both\") {\n      CHECK(emio::reader{\"+-1\"}.parse_int<int>() == emio::err::invalid_data);\n      CHECK(emio::reader{\"-+1\"}.parse_int<unsigned>() == emio::err::invalid_data);\n    }\n  }\n\n  SECTION(\"a failed parse_int keeps previous read position\") {\n    emio::reader reader{\"abc -348648\"};\n    CHECK(reader.read_n_chars(4) == \"abc \");\n    CHECK(reader.parse_int<uint8_t>() == emio::err::out_of_range);\n    CHECK(reader.view_remaining() == \"-348648\");\n    CHECK(reader.parse_int<int16_t>() == emio::err::out_of_range);\n    CHECK(reader.view_remaining() == \"-348648\");\n    CHECK(reader.parse_int<int32_t>() == -348648);\n  }\n\n  SECTION(\"base\") {\n    SECTION(\"positive\") {\n      auto str = \"0159fE\"sv;\n      CHECK(emio::reader{str}.parse_int<int32_t>(10) == 159);\n      CHECK(emio::reader{str}.parse_int<int32_t>(2) == 1);\n      CHECK(emio::reader{str}.parse_int<int32_t>(8) == 13);\n      CHECK(emio::reader{str}.parse_int<int32_t>(16) == 88574);\n    }\n    SECTION(\"negative\") {\n      auto str = \"-1078aB\"sv;\n      CHECK(emio::reader{str}.parse_int<int32_t>(10) == -1078);\n      CHECK(emio::reader{str}.parse_int<int32_t>(2) == -2);\n      CHECK(emio::reader{str}.parse_int<int32_t>(8) == -0107);\n      CHECK(emio::reader{str}.parse_int<int32_t>(16) == -1079467);\n    }\n    SECTION(\"invalid\") {\n      CHECK(emio::reader{\"1\"}.parse_int<int32_t>(1) == emio::err::invalid_argument);\n      CHECK(emio::reader{\"1\"}.parse_int<int32_t>(37) == emio::err::invalid_argument);\n    }\n  }\n}\n\nTEST_CASE(\"reader::read_until\", \"[reader]\") {\n  SECTION(\"read_until_char\") {\n    emio::reader reader{\"a12bcd\"};\n\n    auto res = reader.read_until_char('b');\n    CHECK(res == \"a12\");\n  }\n  SECTION(\"read_until_str\") {\n    emio::reader reader{\"abcabd123\"};\n\n    auto res = reader.read_until_str(\"abd\");\n    CHECK(res == \"abc\");\n  }\n  SECTION(\"read_until_any_of\") {\n    emio::reader reader{\"a12bcd\"};\n\n    auto res = reader.read_until_any_of(\"cb\");\n    CHECK(res == \"a12\");\n  }\n  SECTION(\"read_until_none_of\") {\n    emio::reader reader{\"a12bcd\"};\n\n    auto res = reader.read_until_none_of(\"12a\");\n    CHECK(res == \"a12\");\n  }\n  SECTION(\"read_until\") {\n    emio::reader reader{\"a12bcd\"};\n\n    auto res = reader.read_until([](char c) {\n      return c == 'b';\n    });\n    CHECK(res == \"a12\");\n  }\n}\n\nTEST_CASE(\"reader::read_until_options\", \"[reader]\") {\n  // Test strategy:\n  // * Call one read_until function with all possible combinations of options.\n  // Expected: The options works as expected.\n\n  SECTION(\"with a filled reader\") {\n    emio::reader reader{\"a12bcd\"};\n\n    const bool should_succeed = GENERATE(true, false);\n    const bool include_delimiter = GENERATE(true, false);\n    const bool keep_delimiter = GENERATE(true, false);\n    const bool ignore_eof = GENERATE(true, false);\n    const char delimiter = should_succeed ? 'c' : 'x';\n\n    const emio::reader::read_until_options options{\n        .include_delimiter = include_delimiter,\n        .keep_delimiter = keep_delimiter,\n        .ignore_eof = ignore_eof,\n    };\n    emio::result<std::string_view> res = reader.read_until_char(delimiter, options);\n\n    if (should_succeed) {\n      if (include_delimiter) {\n        CHECK(res == \"a12bc\");\n      } else {\n        CHECK(res == \"a12b\");\n      }\n      if (keep_delimiter) {\n        CHECK(reader.read_remaining() == \"cd\");\n      } else {\n        CHECK(reader.read_remaining() == \"d\");\n      }\n    } else {\n      if (ignore_eof) {\n        CHECK(res == emio::err::invalid_data);\n        CHECK(reader.read_remaining() == \"a12bcd\");\n      } else {\n        CHECK(res == \"a12bcd\");\n        CHECK(reader.read_remaining().empty());\n      }\n    }\n  }\n\n  SECTION(\"with an empty reader\") {\n    emio::reader reader{\"\"};\n\n    const bool ignore_eof = GENERATE(true, false);\n    const emio::reader::read_until_options options{\n        .ignore_eof = ignore_eof,\n    };\n\n    emio::result<std::string_view> res = reader.read_until_char('x', options);\n    CHECK(res == emio::err::eof);\n  }\n\n  SECTION(\"default option works with delimiter and EOF\") {\n    emio::reader reader{\"0;1;2;3;4\"};\n    int i = 0;\n    while (auto result = reader.read_until_char(';')) {\n      // Should return the number without ;.\n      // Parse integer ascending.\n      emio::reader int_parser{result.value()};\n      CHECK(int_parser.parse_int<int>() == i++);\n      CHECK(int_parser.eof());\n    }\n    CHECK(i == 5);\n  }\n}\n\nTEST_CASE(\"reader::subreader\", \"[reader]\") {\n  SECTION(\"from empty reader\") {\n    emio::reader rdr;\n\n    SECTION(\"defaulted\") {\n      emio::result<emio::reader> res = rdr.subreader(0);\n      REQUIRE(res);\n      CHECK(res->eof());\n\n      res->unpop();\n      CHECK(res->eof());\n\n      res->pop();\n      CHECK(res->eof());\n    }\n    SECTION(\"normal\") {\n      const size_t len = GENERATE(0U, 1U, 2U, emio::reader::npos);\n\n      emio::result<emio::reader> res = rdr.subreader(0, len);\n      REQUIRE(res);\n      CHECK(res->eof());\n    }\n    SECTION(\"eof\") {\n      CHECK(rdr.subreader(1, 0) == emio::err::eof);\n    }\n  }\n  SECTION(\"from non-empty reader\") {\n    constexpr std::string_view expected{\"abc\"};\n    emio::reader rdr{expected};\n\n    SECTION(\"defaulted\") {\n      emio::result<emio::reader> res = rdr.subreader(0);\n      REQUIRE(res);\n      CHECK(res->view_remaining() == expected);\n\n      res->unpop();\n      CHECK(res->view_remaining() == expected);\n\n      res->pop();\n      CHECK(res->view_remaining() == expected.substr(1));\n    }\n    SECTION(\"normal\") {\n      const size_t pos = GENERATE(0U, 1U, 2U, 3U);\n      const size_t len = GENERATE(0U, 1U, 2U, 3U, emio::reader::npos);\n\n      emio::result<emio::reader> res = rdr.subreader(pos, len);\n      REQUIRE(res);\n      CHECK(res->view_remaining() == expected.substr(pos, len));\n    }\n    SECTION(\"eof\") {\n      CHECK(rdr.subreader(expected.size() + 1) == emio::err::eof);\n    }\n  }\n  SECTION(\"from non-empty reader after some reading\") {\n    emio::reader rdr{\"123abc\"};\n    REQUIRE(rdr.read_n_chars(3));\n    constexpr std::string_view expected{\"abc\"};\n\n    SECTION(\"defaulted\") {\n      emio::result<emio::reader> res = rdr.subreader(0);\n      REQUIRE(res);\n      CHECK(res->view_remaining() == expected);\n\n      res->unpop();\n      CHECK(res->view_remaining() == expected);\n\n      res->pop();\n      CHECK(res->view_remaining() == expected.substr(1));\n    }\n    SECTION(\"normal\") {\n      const size_t pos = GENERATE(0U, 1U, 2U, 3U);\n      const size_t len = GENERATE(0U, 1U, 2U, 3U, emio::reader::npos);\n\n      emio::result<emio::reader> res = rdr.subreader(pos, len);\n      REQUIRE(res);\n      CHECK(res->view_remaining() == expected.substr(pos, len));\n    }\n    SECTION(\"eof\") {\n      CHECK(rdr.subreader(expected.size() + 1) == emio::err::eof);\n    }\n  }\n}\n"
  },
  {
    "path": "test/unit_test/test_result.cpp",
    "content": "// Unit under test.\n#include <emio/result.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <catch2/generators/catch_generators.hpp>\n#include <catch2/matchers/catch_matchers.hpp>\n\nTEST_CASE(\"result<T = int>\", \"[result]\") {\n  // Test strategy:\n  // * Construct a result<int> with an integer or an error.\n  // * Test all result<int> methods.\n  // Expected: Every method works as expected.\n\n  SECTION(\"succeeded\") {\n    emio::result<int> res{1};\n\n    REQUIRE(res.has_value());\n    CHECK_NOTHROW(res.assume_value() == 1);\n    CHECK_NOTHROW(res.value() == 1);\n\n    CHECK_FALSE(res.has_error());\n    // CHECK_NOTHROW(res.assume_error()); // Would be illegal.\n    CHECK_THROWS_AS(res.error(), emio::bad_result_access);\n    CHECK_THROWS_WITH(res.error(), \"no error\");\n  }\n\n  SECTION(\"failed\") {\n    emio::result<int> res{emio::err::eof};\n\n    CHECK_FALSE(res.has_value());\n    // CHECK_NOTHROW(res.assume_value()); // Would be illegal.\n    CHECK_THROWS_AS(res.value(), emio::bad_result_access);\n    CHECK_THROWS_WITH(res.value(), \"eof\");\n\n    REQUIRE(res.has_error());\n    CHECK(res.assume_error() == emio::err::eof);\n    CHECK(res.error() == emio::err::eof);\n  }\n\n  SECTION(\"comparison\") {\n    emio::result<int> res_with_1{1};\n    emio::result<int> res_with_2{2};\n    emio::result<int> res_with_error_1{emio::err::eof};\n    emio::result<int> res_with_error_2{emio::err::invalid_argument};\n\n    CHECK(res_with_1 == res_with_1);\n    CHECK_FALSE(res_with_1 != res_with_1);\n\n    CHECK(res_with_error_1 == res_with_error_1);\n    CHECK_FALSE(res_with_error_1 != res_with_error_1);\n\n    CHECK_FALSE(res_with_error_1 == res_with_error_2);\n    CHECK(res_with_error_1 != res_with_error_2);\n\n    CHECK_FALSE(res_with_1 == res_with_error_1);\n    CHECK_FALSE(res_with_error_1 == res_with_1);\n    CHECK(res_with_1 != res_with_error_1);\n    CHECK(res_with_error_1 != res_with_1);\n\n    CHECK_FALSE(res_with_1 == res_with_2);\n    CHECK(res_with_1 != res_with_2);\n\n    CHECK(res_with_1 == 1);\n    CHECK(1 == res_with_1);\n    CHECK_FALSE(res_with_1 != 1);\n    CHECK_FALSE(1 != res_with_1);\n\n    CHECK_FALSE(res_with_2 == 1);\n    CHECK_FALSE(1 == res_with_2);\n    CHECK(res_with_2 != 1);\n    CHECK(1 != res_with_2);\n\n    CHECK_FALSE(res_with_1 == emio::err::eof);\n    CHECK(res_with_1 != emio::err::eof);\n    CHECK_FALSE(res_with_error_1 == 1);\n    CHECK(res_with_error_1 != 1);\n  }\n}\n\nTEST_CASE(\"result<std::string>\", \"[result]\") {\n  // Test strategy:\n  // * Construct a result<std::string> with success.\n  // * Test all constructor and value access methods with different value categories.\n  // Expected: Every method works as expected.\n\n  constexpr std::string_view initial{\"abc\"};\n\n  emio::result<std::string> res{initial};\n  REQUIRE(res.has_value());\n  CHECK(res == initial);\n\n  SECTION(\"mutable lvalue\") {\n    res.value().clear();\n    res.assume_value() += 'd';\n    res->push_back('e');\n    *res += 'f';\n\n    CHECK(res == \"def\");\n  }\n\n  SECTION(\"const lvalue\") {\n    const emio::result<std::string> const_res = res;\n\n    CHECK(const_res.value().size() == 3);\n    CHECK(const_res.assume_value().size() == 3);\n    CHECK(const_res->size() == 3);\n    CHECK((*const_res).size() == 3);\n  }\n\n  SECTION(\"mutable rvalue\") {\n    std::string value;\n\n    SECTION(\"assume_value\") {\n      value = std::move(res).assume_value();\n    }\n    SECTION(\"value\") {\n      value = std::move(res).value();\n    }\n    SECTION(\"operator*\") {\n      value = *std::move(res);\n    }\n\n    CHECK(value != res);\n    CHECK(value == initial);\n    CHECK(res->empty());\n  }\n\n  SECTION(\"const rvalue\") {\n    const emio::result<std::string> const_res = res;\n\n    CHECK(std::move(const_res).value().size() == 3);\n    CHECK(std::move(const_res).assume_value().size() == 3);\n    CHECK(std::move(const_res)->size() == 3);\n    CHECK((*std::move(const_res)).size() == 3);\n  }\n\n  SECTION(\"value_or\") {\n    SECTION(\"when *this has a value\") {\n      emio::result<std::string> s{\"abc\"};\n\n      CHECK(s.value_or(\"def\") == \"abc\");\n      CHECK(std::move(s).value_or(\"def\") == \"abc\");\n    }\n    SECTION(\"when *this has no value\") {\n      emio::result<std::string> s{emio::err::invalid_data};\n\n      CHECK(s.value_or(\"def\") == \"def\");\n      CHECK(std::move(s).value_or(\"def\") == \"def\");\n    }\n  }\n}\n\nTEST_CASE(\"emio::err to_string()\", \"[result]\") {\n  // Test strategy:\n  // * Call to_string(emio::err) with all defined error codes.\n  // Expected: Correct string is returned.\n\n  CHECK(to_string(emio::err::eof) == \"eof\");\n  CHECK(to_string(emio::err::invalid_argument) == \"invalid argument\");\n  CHECK(to_string(emio::err::invalid_data) == \"invalid data\");\n  CHECK(to_string(emio::err::invalid_format) == \"invalid format\");\n  CHECK(to_string(emio::err::out_of_range) == \"out of range\");\n}\n\nTEST_CASE(\"EMIO_TRY/V macro\", \"[result]\") {\n  // Test strategy:\n  // * Call different lambda function returning either a success or an error.\n  // * Use EMIO_TRY or EMIO_TRYV for error handling.\n  // Expected: Success, value and error is correctly propagate.\n\n  const bool should_succeed = GENERATE(true, false);\n\n  auto func_return_float = [=]() -> emio::result<float> {\n    if (should_succeed) {\n      return 1.1F;\n    }\n    return emio::err::eof;\n  };\n\n  if (should_succeed) {\n    CHECK(func_return_float() == 1.1F);\n  } else {\n    CHECK(func_return_float() == emio::err::eof);\n  }\n\n  auto func_return_int = [=]() -> emio::result<int> {\n    EMIO_TRYV(func_return_float());\n    return 1;\n  };\n\n  if (should_succeed) {\n    CHECK(func_return_int() == 1);\n  } else {\n    CHECK(func_return_int() == emio::err::eof);\n  }\n\n  auto func_return_char = [=]() -> emio::result<char> {\n    EMIO_TRYV(func_return_int());\n    return '1';\n  };\n\n  if (should_succeed) {\n    CHECK(func_return_char() == '1');\n  } else {\n    CHECK(func_return_char() == emio::err::eof);\n  }\n\n  auto func_return_void = [=]() -> emio::result<void> {\n    EMIO_TRY(char g, func_return_char());\n    if (g == '1') {\n      return emio::success;\n    } else {\n      return emio::err::invalid_data;\n    }\n  };\n\n  if (should_succeed) {\n    CHECK(func_return_void());\n  } else {\n    CHECK(func_return_void() == emio::err::eof);\n  }\n}\n"
  },
  {
    "path": "test/unit_test/test_scan.cpp",
    "content": "// Unit under test.\n#include <emio/scan.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\n#include \"integer_ranges.hpp\"\n\nnamespace {\n\ntemplate <typename... Args>\nbool validate_scan_string(std::string_view str) {\n  return emio::detail::scan::scan_trait::validate_string<Args...>(str);\n}\n\n}  // namespace\n\nTEST_CASE(\"scan API\", \"[scan]\") {\n  SECTION(\"no args\") {\n    CHECK(emio::scan(\"abc{\", \"abc{{\"));\n  }\n  SECTION(\"normal\") {\n    unsigned int a = 0;\n    int b = 0;\n    char c;\n    REQUIRE(emio::scan(\"1,-2!\", \"{},{}{}\", a, b, c));\n    CHECK(a == 1);\n    CHECK(b == -2);\n    CHECK(c == '!');\n  }\n  SECTION(\"runtime string\") {\n    unsigned int a = 0;\n    int b = 0;\n    char c;\n\n    REQUIRE(emio::scan(\"1,-2!\", emio::runtime(\"{},{}{}\"), a, b, c));\n    CHECK(a == 1);\n    CHECK(b == -2);\n    CHECK(c == '!');\n  }\n  SECTION(\"compile-time\") {\n    constexpr bool success = [] {\n      unsigned int a = 0;\n      int b = 0;\n      char c;\n\n      emio::result<void> res = emio::scan(\"1,-2!\", emio::runtime(\"{},{}{}\"), a, b, c);\n      return res && (a == 1) && (b == -2) && (c == '!');\n    }();\n    STATIC_CHECK(success);\n  }\n}\n\nTEST_CASE(\"vscan API\", \"[scan]\") {\n  SECTION(\"no args\") {\n    CHECK(emio::vscan(\"abc{\", emio::make_scan_args(\"abc{{\")));\n  }\n  SECTION(\"normal\") {\n    unsigned int a = 0;\n    int b = 0;\n    char c;\n\n    REQUIRE(emio::vscan(\"1,-2!\", emio::make_scan_args(\"{},{}{}\", a, b, c)));\n    CHECK(a == 1);\n    CHECK(b == -2);\n    CHECK(c == '!');\n  }\n  SECTION(\"runtime string\") {\n    unsigned int a = 0;\n    int b = 0;\n    char c;\n\n    REQUIRE(emio::vscan(\"1,-2!\", emio::make_scan_args(emio::runtime(\"{},{}{}\"), a, b, c)));\n    CHECK(a == 1);\n    CHECK(b == -2);\n    CHECK(c == '!');\n  }\n}\n\nTEST_CASE(\"scan_from API\", \"[scan]\") {\n  SECTION(\"no args and no escapes\") {\n    emio::reader rdr(\"abc\");\n    CHECK(emio::scan_from(rdr, \"abc\"));\n    CHECK(rdr.eof());\n  }\n  SECTION(\"no args and escapes\") {\n    emio::reader rdr(\"abc{\");\n    CHECK(emio::scan_from(rdr, \"abc{{\"));\n    CHECK(rdr.eof());\n  }\n  SECTION(\"normal\") {\n    unsigned int a = 0;\n    int b = 0;\n    char c;\n\n    emio::reader rdr(\"1,-2!rest\");\n    REQUIRE(emio::scan_from(rdr, \"{},{}{}\", a, b, c));\n    CHECK(a == 1);\n    CHECK(b == -2);\n    CHECK(c == '!');\n    CHECK(rdr.read_remaining() == \"rest\");\n  }\n  SECTION(\"runtime string\") {\n    unsigned int a = 0;\n    int b = 0;\n    char c;\n\n    emio::reader rdr(\"1,-2!rest\");\n    REQUIRE(emio::scan_from(rdr, emio::runtime(\"{},{}{}\"), a, b, c));\n    CHECK(a == 1);\n    CHECK(b == -2);\n    CHECK(c == '!');\n    CHECK(rdr.read_remaining() == \"rest\");\n  }\n  SECTION(\"compile time\") {\n    constexpr bool success = [] {\n      emio::reader rdr(\"abc\");\n      emio::result<void> res = emio::scan_from(rdr, emio::runtime(\"abc\"));\n      return res && rdr.eof();\n    }();\n    STATIC_CHECK(success);\n\n    constexpr bool success2 = [] {\n      unsigned int a = 0;\n      int b = 0;\n      char c;\n\n      emio::reader rdr(\"1,-2!rest\");\n      emio::result<void> res = emio::scan_from(rdr, emio::runtime(\"{},{}{}\"), a, b, c);\n      return res && (a == 1) && (b == -2) && (c == '!') && (rdr.read_remaining() == \"rest\");\n    }();\n    STATIC_CHECK(success2);\n  }\n}\n\nTEST_CASE(\"vscan_from API\", \"[scan]\") {\n  SECTION(\"normal\") {\n    unsigned int a = 0;\n    int b = 0;\n    char c;\n\n    emio::reader rdr(\"1,-2!rest\");\n    REQUIRE(emio::vscan_from(rdr, emio::make_scan_args(\"{},{}{}\", a, b, c)));\n    CHECK(a == 1);\n    CHECK(b == -2);\n    CHECK(c == '!');\n    CHECK(rdr.read_remaining() == \"rest\");\n  }\n  SECTION(\"runtime string\") {\n    unsigned int a = 0;\n    int b = 0;\n    char c;\n\n    emio::reader rdr(\"1,-2!rest\");\n    REQUIRE(emio::vscan_from(rdr, emio::make_scan_args(emio::runtime(\"{},{}{}\"), a, b, c)));\n    CHECK(a == 1);\n    CHECK(b == -2);\n    CHECK(c == '!');\n    CHECK(rdr.read_remaining() == \"rest\");\n  }\n}\n\nTEST_CASE(\"format scan string\", \"[scan]\") {\n  SECTION(\"compile-time validation\") {\n    emio::format_scan_string<int> str{\"{}\"};\n    CHECK(str.get() == \"{}\");\n  }\n  SECTION(\"runtime validation\") {\n    emio::format_scan_string<int> str{emio::runtime(\"{}\")};\n    CHECK(str.get() == \"{}\");\n  }\n  SECTION(\"failed runtime validation\") {\n    emio::format_scan_string<int> str{emio::runtime(\"{\")};\n    CHECK(str.get() == emio::err::invalid_format);\n  }\n}\n\nTEST_CASE(\"incomplete scan\", \"[scan]\") {\n  int a = 0;\n  int b = 0;\n\n  SECTION(\"without reader\") {\n    CHECK(!emio::scan(\"1,-2rest\", \"{},{}\", a, b));\n  }\n  SECTION(\"vscan\") {\n    CHECK(!emio::vscan(\"1,-2rest\", emio::make_scan_args(\"{},{}\", a, b)));\n  }\n  SECTION(\"with reader\") {\n    emio::reader rdr(\"1,-2rest\");\n    CHECK(emio::scan_from(rdr, \"{},{}\", a, b));\n    CHECK(rdr.read_remaining() == \"rest\");\n  }\n}\n\nTEST_CASE(\"scan_char\", \"[scan]\") {\n  char c;\n  REQUIRE(emio::scan(\"o\", \"{}\", c));\n  CHECK(c == 'o');\n\n  REQUIRE(emio::scan(\"k\", \"{:c}\", c));\n  CHECK(c == 'k');\n\n  REQUIRE(emio::scan(\"f\", \"{:1}\", c));\n  CHECK(c == 'f');\n\n  REQUIRE(emio::scan(\"g\", \"{:1c}\", c));\n  CHECK(c == 'g');\n\n  CHECK(validate_scan_string<char>(\"{:c}\"));\n  CHECK(!validate_scan_string<char>(\"{:0}\"));\n  CHECK(!validate_scan_string<char>(\"{:2}\"));\n  CHECK(!validate_scan_string<char>(\"{:d}\"));\n  CHECK(!validate_scan_string<char>(\"{:#}\"));\n}\n\nTEST_CASE(\"detect base\", \"[scan]\") {\n  int val{};\n\n  SECTION(\"binary\") {\n    REQUIRE(emio::scan(\"0b1011\", \"{:#}\", val));\n    CHECK(val == 0b1011);\n\n    REQUIRE(emio::scan(\"0B101\", \"{:#}\", val));\n    CHECK(val == 0b101);\n\n    REQUIRE(emio::scan(\"+0b1010\", \"{:#}\", val));\n    CHECK(val == +0b1010);\n\n    REQUIRE(emio::scan(\"-0b111\", \"{:#}\", val));\n    CHECK(val == -0b111);\n  }\n  SECTION(\"octal\") {\n    REQUIRE(emio::scan(\"0175\", \"{:#}\", val));\n    CHECK(val == 0175);\n\n    REQUIRE(emio::scan(\"+054\", \"{:#}\", val));\n    CHECK(val == +054);\n\n    REQUIRE(emio::scan(\"-0023\", \"{:#}\", val));\n    CHECK(val == -023);\n  }\n  SECTION(\"decimal\") {\n    REQUIRE(emio::scan(\"-0\", \"{:#}\", val));\n    CHECK(val == 0);\n\n    REQUIRE(emio::scan(\"0\", \"{:#}\", val));\n    CHECK(val == 0);\n\n    REQUIRE(emio::scan(\"1\", \"{:#}\", val));\n    CHECK(val == 1);\n\n    REQUIRE(emio::scan(\"+1\", \"{:#}\", val));\n    CHECK(val == +1);\n\n    REQUIRE(emio::scan(\"-1\", \"{:#}\", val));\n    CHECK(val == -1);\n  }\n  SECTION(\"hex\") {\n    REQUIRE(emio::scan(\"0x58\", \"{:#}\", val));\n    CHECK(val == 0x58);\n\n    REQUIRE(emio::scan(\"0XAe\", \"{:#}\", val));\n    CHECK(val == 0xAE);\n\n    REQUIRE(emio::scan(\"+0x5\", \"{:#}\", val));\n    CHECK(val == +0x5);\n\n    REQUIRE(emio::scan(\"-0x1\", \"{:#}\", val));\n    CHECK(val == -0x1);\n  }\n  SECTION(\"invalid\") {\n    CHECK(emio::scan(\"-\", \"{:#}\", val) == emio::err::eof);\n    CHECK(emio::scan(\"--1\", \"{:#}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"a\", \"{:#}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0xx\", \"{:#}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0x+1\", \"{:#}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0x-1\", \"{:#}\", val) == emio::err::invalid_data);\n  }\n  SECTION(\"unsigned\") {\n    unsigned int uval{};\n    CHECK(emio::scan(\"+5\", \"{:#}\", uval));\n    CHECK(uval == 5);\n\n    CHECK(emio::scan(\"-1\", \"{:#}\", uval) == emio::err::out_of_range);\n    CHECK(emio::scan(\"-0x5\", \"{:#}\", uval) == emio::err::out_of_range);\n  }\n\n  CHECK(validate_scan_string<int>(\"{:#x}\"));\n  CHECK(!validate_scan_string<int>(\"{:d#}\"));\n  CHECK(!validate_scan_string<int>(\"{:f}\"));\n  CHECK(!validate_scan_string<int>(\"{:.5}\"));\n}\n\nTEST_CASE(\"integral with width\", \"[scan]\") {\n  int val{};\n  int val2{};\n\n  REQUIRE(emio::scan(\"1524\", \"{:2}{:2}\", val, val2));\n  CHECK(val == 15);\n  CHECK(val2 == 24);\n\n  REQUIRE(emio::scan(\"+0x5\", \"{:#4}\", val));\n  CHECK(val == 5);\n\n  REQUIRE(emio::scan(\"+0x5\", \"{:#2}x{}\", val, val2));\n  CHECK(val == 0);\n  CHECK(val2 == 5);\n\n  REQUIRE(emio::scan(\"12ab\", \"{:4}\", val) == emio::err::invalid_data);\n  REQUIRE(emio::scan(\"+0x5\", \"{:#1}0x5\", val) == emio::err::eof);\n  REQUIRE(emio::scan(\"+0x5\", \"{:#6}\", val) == emio::err::eof);\n  REQUIRE(emio::scan(\"+0x5\", \"{:#3}5\", val) == emio::err::eof);\n\n  CHECK(!validate_scan_string<int>(\"{:+5}\"));\n  CHECK(!validate_scan_string<int>(\"{:-5}\"));\n}\n\nTEST_CASE(\"scan integral of different types\", \"[scan]\") {\n  const auto range_check = []<typename T>(std::type_identity<T> /*type*/, const auto& lower_input,\n                                          const auto& upper_input) {\n    if constexpr (!std::is_same_v<T, bool>) {\n      T val{};\n      REQUIRE(emio::scan(std::get<0>(lower_input), \"{}\", val));\n      CHECK(val == std::numeric_limits<T>::min() + 1);\n      REQUIRE(emio::scan(std::get<1>(lower_input), \"{}\", val));\n      CHECK(val == std::numeric_limits<T>::min());\n      REQUIRE(emio::scan(std::get<2>(lower_input), \"{}\", val) == emio::err::out_of_range);\n      REQUIRE(emio::scan(std::get<3>(lower_input), \"{}\", val) == emio::err::out_of_range);\n\n      REQUIRE(emio::scan(std::get<0>(upper_input), \"{}\", val));\n      CHECK(val == std::numeric_limits<T>::max() - 1);\n      REQUIRE(emio::scan(std::get<1>(upper_input), \"{}\", val));\n      CHECK(val == std::numeric_limits<T>::max());\n      REQUIRE(emio::scan(std::get<2>(upper_input), \"{}\", val) == emio::err::out_of_range);\n      REQUIRE(emio::scan(std::get<3>(upper_input), \"{}\", val) == emio::err::out_of_range);\n    }\n  };\n  emio::test::apply_integer_ranges(range_check);\n}\n\nTEST_CASE(\"scan_binary\", \"[scan]\") {\n  int val{};\n  SECTION(\"no prefix\") {\n    REQUIRE(emio::scan(\"1011\", \"{:b}\", val));\n    CHECK(val == 0b1011);\n\n    REQUIRE(emio::scan(\"+1001\", \"{:b}\", val));\n    CHECK(val == +0b1001);\n\n    REQUIRE(emio::scan(\"-11\", \"{:b}\", val));\n    CHECK(val == -0b11);\n  }\n  SECTION(\"invalid number\") {\n    CHECK_FALSE(emio::scan(\"2\", \"{:b}\", val));\n  }\n  SECTION(\"with prefix\") {\n    REQUIRE(emio::scan(\"0b10\", \"{:#b}\", val));\n    CHECK(val == 0b10);\n\n    REQUIRE(emio::scan(\"0B110\", \"{:#b}\", val));\n    CHECK(val == 0b110);\n\n    REQUIRE(emio::scan(\"+0b101\", \"{:#b}\", val));\n    CHECK(val == +0b101);\n\n    REQUIRE(emio::scan(\"-0b101\", \"{:#b}\", val));\n    CHECK(val == -0b101);\n  }\n  SECTION(\"wrong prefix\") {\n    CHECK(emio::scan(\"0\", \"{:#b}\", val) == emio::err::eof);\n    CHECK(emio::scan(\"0o\", \"{:#b}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0x1\", \"{:#b}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0b+1\", \"{:#b}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0b-1\", \"{:#b}\", val) == emio::err::invalid_data);\n  }\n}\n\nTEST_CASE(\"scan_octal\", \"[scan]\") {\n  int val{};\n  SECTION(\"no prefix\") {\n    REQUIRE(emio::scan(\"75\", \"{:o}\", val));\n    CHECK(val == 075);\n\n    REQUIRE(emio::scan(\"+12\", \"{:o}\", val));\n    CHECK(val == +012);\n\n    REQUIRE(emio::scan(\"-62\", \"{:o}\", val));\n    CHECK(val == -062);\n  }\n  SECTION(\"invalid number\") {\n    CHECK(emio::scan(\"8\", \"{:o}\", val) == emio::err::invalid_data);\n  }\n  SECTION(\"with prefix\") {\n    REQUIRE(emio::scan(\"00\", \"{:#o}\", val));\n    CHECK(val == 00);\n\n    REQUIRE(emio::scan(\"050\", \"{:#o}\", val));\n    CHECK(val == 050);\n\n    REQUIRE(emio::scan(\"+050\", \"{:#o}\", val));\n    CHECK(val == +050);\n\n    REQUIRE(emio::scan(\"-050\", \"{:#o}\", val));\n    CHECK(val == -050);\n  }\n  SECTION(\"wrong prefix\") {\n    CHECK(emio::scan(\"0\", \"{:#o}\", val) == emio::err::eof);\n    CHECK(emio::scan(\"0o\", \"{:#o}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0+0\", \"{:#o}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0-0\", \"{:#o}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0b3\", \"{:#o}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0x3\", \"{:#o}\", val) == emio::err::invalid_data);\n  }\n}\n\nTEST_CASE(\"scan_decimal\", \"[scan]\") {\n  int val{};\n  SECTION(\"no prefix\") {\n    REQUIRE(emio::scan(\"49\", \"{}\", val));\n    CHECK(val == 49);\n\n    REQUIRE(emio::scan(\"-87\", \"{}\", val));\n    CHECK(val == -87);\n\n    REQUIRE(emio::scan(\"59849\", \"{:d}\", val));\n    CHECK(val == 59849);\n\n    REQUIRE(emio::scan(\"+47891\", \"{:d}\", val));\n    CHECK(val == +47891);\n\n    REQUIRE(emio::scan(\"-259\", \"{:d}\", val));\n    CHECK(val == -259);\n  }\n  SECTION(\"invalid number\") {\n    CHECK(emio::scan(\"a\", \"{:d}\", val) == emio::err::invalid_data);\n  }\n  SECTION(\"with prefix\") {\n    REQUIRE(emio::scan(\"42\", \"{:#d}\", val));\n    CHECK(val == 42);\n\n    REQUIRE(emio::scan(\"240\", \"{:#d}\", val));\n    CHECK(val == 240);\n\n    REQUIRE(emio::scan(\"+42\", \"{:#d}\", val));\n    CHECK(val == +42);\n\n    REQUIRE(emio::scan(\"-42\", \"{:#d}\", val));\n    CHECK(val == -42);\n  }\n  SECTION(\"edge cases\") {\n    val = -1;\n    SECTION(\"test 1\") {\n      emio::reader rdr{\"0x5\"};\n      REQUIRE(emio::scan_from(rdr, \"{}\", val));\n      CHECK(val == 0);\n      CHECK(rdr.read_remaining() == \"x5\");\n    }\n\n    SECTION(\"test 2\") {\n      emio::reader rdr{\"0b3\"};\n      REQUIRE(emio::scan_from(rdr, \"{:#d}\", val));\n      CHECK(val == 0);\n      CHECK(rdr.read_remaining() == \"b3\");\n    }\n\n    SECTION(\"test 3\") {\n      REQUIRE(emio::scan(\"0\", \"{:#d}\", val));\n      CHECK(val == 0);\n    }\n  }\n  SECTION(\"overflows\") {\n    SECTION(\"signed\") {\n      int8_t val8{};\n      CHECK(emio::scan(\"127\", \"{}\", val8));\n      CHECK(val8 == 127);\n\n      CHECK(emio::scan(\"128\", \"{}\", val8) == emio::err::out_of_range);\n\n      REQUIRE(emio::scan(\"-128\", \"{}\", val8));\n      CHECK(val8 == -128);\n\n      CHECK(emio::scan(\"-129\", \"{}\", val8) == emio::err::out_of_range);\n    }\n    SECTION(\"unsigned\") {\n      uint8_t uval8{};\n      REQUIRE(emio::scan(\"255\", \"{}\", uval8));\n      CHECK(uval8 == 255);\n\n      CHECK(emio::scan(\"256\", \"{}\", uval8) == emio::err::out_of_range);\n    }\n  }\n}\n\nTEST_CASE(\"scan_hex\", \"[scan]\") {\n  int val{};\n  SECTION(\"no prefix\") {\n    REQUIRE(emio::scan(\"Ad\", \"{:x}\", val));\n    CHECK(val == 0xAD);\n\n    REQUIRE(emio::scan(\"+fe\", \"{:x}\", val));\n    CHECK(val == +0xfe);\n\n    REQUIRE(emio::scan(\"-1d\", \"{:x}\", val));\n    CHECK(val == -0x1d);\n  }\n  SECTION(\"invalid number\") {\n    CHECK(emio::scan(\"g\", \"{:x}\", val) == emio::err::invalid_data);\n  }\n  SECTION(\"with prefix\") {\n    REQUIRE(emio::scan(\"0xdf\", \"{:#x}\", val));\n    CHECK(val == 0xdf);\n\n    REQUIRE(emio::scan(\"0X1e\", \"{:#x}\", val));\n    CHECK(val == 0x1e);\n\n    REQUIRE(emio::scan(\"+0x1e\", \"{:#x}\", val));\n    CHECK(val == +0x1e);\n\n    REQUIRE(emio::scan(\"-0x1e\", \"{:#x}\", val));\n    CHECK(val == -0x1e);\n  }\n  SECTION(\"wrong prefix\") {\n    CHECK(emio::scan(\"da\", \"{:#x}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0b3\", \"{:#x}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0o3\", \"{:#x}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0x+3\", \"{:#x}\", val) == emio::err::invalid_data);\n    CHECK(emio::scan(\"0x-3\", \"{:#x}\", val) == emio::err::invalid_data);\n  }\n}\n\nTEST_CASE(\"scan_string\", \"[scan]\") {\n  std::string s;\n  SECTION(\"until eof\") {\n    REQUIRE(emio::scan(\"abc\", \"{}\", s));\n    CHECK(s == \"abc\");\n\n    REQUIRE(emio::scan(\"abc\", \"a{}\", s));\n    CHECK(s == \"bc\");\n\n    REQUIRE(emio::scan(\"abc\", \"ab{}\", s));\n    CHECK(s == \"c\");\n\n    REQUIRE(emio::scan(\"abc\", \"abc{}\", s));\n    CHECK(s.empty());\n  }\n  SECTION(\"until given size\") {\n    REQUIRE(emio::scan(\"abc\", \"{:3}\", s));\n    CHECK(s == \"abc\");\n\n    REQUIRE(emio::scan(\"aaa\", \"{:2}a\", s));\n    CHECK(s == \"aa\");\n\n    REQUIRE(emio::scan(\"abc\", \"{:4}\", s) == emio::err::eof);\n    REQUIRE(emio::scan(\"abc\", \"{:3}c\", s) == emio::err::eof);\n  }\n  SECTION(\"until next\") {\n    std::string s2;\n    REQUIRE(emio::scan(\"abc\", \"{}b{}\", s, s2));\n    CHECK(s == \"a\");\n    CHECK(s2 == \"c\");\n\n    REQUIRE(emio::scan(\"abc\", \"{}abc\", s));\n    CHECK(s.empty());\n\n    REQUIRE(emio::scan(\"abc\", \"{}{}c\", s, s2));\n    CHECK(s.empty());\n    CHECK(s2 == \"ab\");\n\n    REQUIRE(emio::scan(\"abc\", \"{:2}{}c\", s, s2));\n    CHECK(s == \"ab\");\n    CHECK(s2.empty());\n\n    REQUIRE(emio::scan(\"abc\", \"{:1}{}c\", s, s2));\n    CHECK(s == \"a\");\n    CHECK(s2 == \"b\");\n  }\n  SECTION(\"complex\") {\n    std::string s2;\n    REQUIRE(emio::scan(\"abc{\", \"{}{{\", s));\n    CHECK(s == \"abc\");\n\n    REQUIRE(emio::scan(\"abc}\", \"{}}}\", s));\n    CHECK(s == \"abc\");\n\n    REQUIRE(emio::scan(\"abc{}def}x\", \"{}}}x\", s));\n    CHECK(s == \"abc{}def\");\n\n    REQUIRE(emio::scan(\"abc{}def}x12\", \"{}}}x{}\", s, s2));\n    CHECK(s == \"abc{}def\");\n    CHECK(s2 == \"12\");\n\n    REQUIRE(emio::scan(\"abc{x\", \"{}{{y\", s) == emio::err::invalid_data);\n  }\n\n  CHECK(validate_scan_string<std::string>(\"{:s}\"));\n  CHECK(validate_scan_string<std::string_view>(\"{:s}\"));\n  CHECK(!validate_scan_string<std::string>(\"{:#}\"));\n  CHECK(!validate_scan_string<std::string>(\"{:d}\"));\n}\n"
  },
  {
    "path": "test/unit_test/test_std.cpp",
    "content": "// Unit under test.\n#include <emio/std.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <emio/format.hpp>\n#include <emio/ranges.hpp>\n\nstruct unformattable {};\n\nTEST_CASE(\"std::optional\") {\n  CHECK(emio::format(\"{}\", std::optional<int>{}) == \"none\");\n  CHECK(emio::format(\"{:x}\", std::optional<int>{42}) == \"optional(2a)\");\n  CHECK(emio::format(\"{:x}\", std::optional<int>{42}) == \"optional(2a)\");\n  CHECK(emio::format(emio::runtime(\"{:x}\"), std::optional<int>{42}) == \"optional(2a)\");\n  CHECK(emio::format(\"{}\", std::optional{std::vector{'h', 'e', 'l', 'l', 'o'}}) ==\n        \"optional(['h', 'e', 'l', 'l', 'o'])\");\n  CHECK(emio::format(\"{::d}\", std::optional{std::vector{'h', 'e', 'l', 'l', 'o'}}) ==\n        \"optional([104, 101, 108, 108, 111])\");\n\n  STATIC_CHECK(emio::is_formattable_v<std::optional<int>>);\n  STATIC_CHECK_FALSE(emio::is_formattable_v<std::optional<unformattable>>);\n}\n\nTEST_CASE(\"std::exception\") {\n  CHECK(emio::format(\"{}\", std::exception{}) == \"std::exception\");\n  CHECK(emio::format(\"{}\", std::runtime_error{\"hello\"}) == \"hello\");\n}\n\nTEST_CASE(\"std::filesystem::path\") {\n  using std::filesystem::path;\n\n  CHECK(emio::format(\"{}\", path{}) == \"\");\n  CHECK(emio::format(\"{}\", path{\"/abc/dev\"}) == \"/abc/dev\");\n  CHECK(emio::format(\"{:x>11}\", path{\"/abc/dev\"}) == \"xxx/abc/dev\");\n  CHECK(emio::format(\"{:x<11?}\", path{\"/abc/dev\"}) == \"\\\"/abc/dev\\\"x\");\n  CHECK(emio::format(emio::runtime(\"{:x<11?}\"), path{\"/abc/dev\"}) == \"\\\"/abc/dev\\\"x\");\n}\n\nnamespace {\n\nstruct throws_on_move {\n  throws_on_move() = default;\n  throws_on_move(throws_on_move&&) {\n    throw std::runtime_error{\"As expected...\"};\n  }\n};\n\nstd::string_view format_as(const throws_on_move&) {\n  throw std::logic_error{\"Shouldn't be called.\"};\n}\n\n}  // namespace\n\nTEST_CASE(\"std::variant\") {\n  std::variant<std::monostate, int, double, char, std::nullptr_t, std::string> v{};\n  CHECK(emio::format(\"{}\", v) == \"variant(monostate)\");\n  CHECK(emio::format(emio::runtime(\"{}\"), v) == \"variant(monostate)\");\n  v = 42;\n  CHECK(emio::format(\"{}\", v) == \"variant(42)\");\n  v = 4.2;\n  CHECK(emio::format(\"{}\", v) == \"variant(4.2)\");\n  v = 'x';\n  CHECK(emio::format(\"{}\", v) == \"variant('x')\");\n  v = nullptr;\n  CHECK(emio::format(\"{}\", v) == \"variant(0x0)\");\n  v = \"abc\";\n  CHECK(emio::format(\"{}\", v) == \"variant(\\\"abc\\\")\");\n\n  SECTION(\"valueless by exception\") {\n    std::variant<std::monostate, throws_on_move> v2;\n    try {\n      throws_on_move thrower;\n      v2.emplace<throws_on_move>(std::move(thrower));\n    } catch (const std::runtime_error&) {\n    }\n    CHECK(emio::format(\"{}\", v2) == \"variant(valueless by exception)\");\n  }\n}\n\n#if defined(__cpp_lib_expected)\nTEST_CASE(\"std::expected\") {\n  STATIC_CHECK_FALSE(emio::is_formattable_v<std::expected<unformattable, int>>);\n  STATIC_CHECK_FALSE(emio::is_formattable_v<std::expected<int, unformattable>>);\n  STATIC_CHECK_FALSE(emio::is_formattable_v<std::expected<unformattable, unformattable>>);\n\n  // Non-void value tests\n  CHECK(emio::format(\"{}\", std::expected<int, std::string>{42}) == \"expected(42)\");\n  CHECK(emio::format(\"{}\", std::expected<int, std::string>{std::unexpected{\"error\"}}) == \"unexpected(\\\"error\\\")\");\n\n  // Complex types\n  CHECK(emio::format(\"{}\", std::expected<std::vector<char>, std::string>{std::vector{'a', 'b'}}) ==\n        \"expected(['a', 'b'])\");\n\n  // void value tests\n  CHECK(emio::format(\"{}\", std::expected<void, std::string>{}) == \"expected()\");\n  CHECK(emio::format(\"{}\", std::expected<void, std::string>{std::unexpected{\"error\"}}) == \"unexpected(\\\"error\\\")\");\n\n  // Nested expected\n  CHECK(emio::format(\"{}\", std::expected<std::expected<int, std::string>, std::string>{\n                               std::expected<int, std::string>{42}}) == \"expected(expected(42))\");\n\n  CHECK(emio::format(\"{}\", std::expected<std::expected<void, std::string>, std::string>{\n                               std::expected<void, std::string>{}}) == \"expected(expected())\");\n}\n#endif\n"
  },
  {
    "path": "test/unit_test/test_writer.cpp",
    "content": "// Unit under test.\n#include <emio/writer.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <functional>\n\nusing namespace std::string_view_literals;\n\nnamespace {\n\nclass buffer_from_cb : public emio::buffer {\n public:\n  void set_cache_cb(std::function<std::span<char>()> cache_cb) noexcept {\n    cache_cb_ = std::move(cache_cb);\n  }\n\n  emio::result<std::span<char>> request_write_area(const size_t /*used*/, const size_t size) noexcept override {\n    const std::span<char> area{cache_cb_()};\n    if (area.empty()) {\n      return emio::err::eof;\n    }\n    this->set_write_area(area);\n    if (size > area.size()) {\n      return area;\n    }\n    return area.subspan(0, size);\n  }\n\n private:\n  std::function<std::span<char>()> cache_cb_;\n};\n\n}  // namespace\n\nTEST_CASE(\"writer\", \"[writer]\") {\n  // Test strategy:\n  // * Construct a writer from a string buffer.\n  // * Write data with the writer into the buffer.\n  // Expected: Every method works as expected.\n\n  emio::memory_buffer buf{};\n  emio::writer writer{buf};\n\n  SECTION(\"get_buffer\") {\n    emio::buffer& buffer = writer.get_buffer();\n    auto area = buffer.get_write_area_of(1);\n    REQUIRE(area);\n    (*area)[0] = '1';\n    CHECK(buf.view() == \"1\");\n  }\n  SECTION(\"write_char\") {\n    CHECK(writer.write_char('c'));\n    CHECK(buf.view() == \"c\");\n  }\n  SECTION(\"write_char_n\") {\n    CHECK(writer.write_char_n('c', 3));\n    CHECK(buf.view() == \"ccc\");\n  }\n  SECTION(\"write_char_escaped\") {\n    CHECK(writer.write_char_escaped('\"'));\n    CHECK(buf.view() == \"'\\\\\\\"'\");\n  }\n  SECTION(\"write_str\") {\n    CHECK(writer.write_str(\"s\"));\n    CHECK(buf.view() == \"s\");\n  }\n  SECTION(\"write_str_escaped\") {\n    SECTION(\"normal\") {\n      CHECK(writer.write_str_escaped(\"'\"));\n      CHECK(buf.view() == \"\\\"\\\\'\\\"\");\n    }\n    SECTION(\"empty\") {\n      CHECK(writer.write_str_escaped(\"\"));\n      CHECK(buf.view() == \"\\\"\\\"\");\n    }\n  }\n  SECTION(\"write_int\") {\n    CHECK(writer.write_int(1));\n    CHECK(buf.view() == \"1\");\n\n    CHECK(writer.write_int(15, {.base = 16}));\n    CHECK(buf.view() == \"1f\");\n\n    CHECK(writer.write_int(10, {.base = 11, .upper_case = true}));\n    CHECK(buf.view() == \"1fA\");\n\n    CHECK(writer.write_int(-3, {.base = 2}));\n    CHECK(buf.view() == \"1fA-11\");\n\n    CHECK(writer.write_int(3, {.base = 1}) == emio::err::invalid_argument);\n    CHECK(writer.write_int(3, {.base = 37}) == emio::err::invalid_argument);\n    CHECK(buf.view() == \"1fA-11\");\n\n    CHECK(writer.write_int(9999));\n    CHECK(buf.view() == \"1fA-119999\");\n\n    CHECK(writer.write_int(414, {.base = 15}));\n    CHECK(buf.view() == \"1fA-1199991c9\");\n  }\n  SECTION(\"write_int zeros\") {\n    CHECK(writer.write_int(0));\n    CHECK(buf.view() == \"0\");\n\n    CHECK(writer.write_int(0, {.base = 16}));\n    CHECK(buf.view() == \"00\");\n\n    CHECK(writer.write_int(0, {.base = 8}));\n    CHECK(buf.view() == \"000\");\n\n    CHECK(writer.write_int(0, {.base = 2}));\n    CHECK(buf.view() == \"0000\");\n\n    CHECK(writer.write_int(0, {.base = 36}));\n    CHECK(buf.view() == \"00000\");\n  }\n}\n\nTEST_CASE(\"writer with cached buffer\", \"[writer]\") {\n  // Test strategy:\n  // * Construct a writer from a cached buffer (output iterator).\n  // * Write long strings (> emio::default_cache_size) with the writer into the buffer.\n  // Expected: The string methods work as expected because the string is written in chunks.\n\n  const std::string expected_str_part_1(emio::default_cache_size + 1, 'x');\n  const std::string expected_str_part_2{\n      \"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et \"\n      \"dolor e magna aliquyam erat, sed diam voluptua.\"};\n  const std::string expected_str_part_3{\n      \"At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est \"\n      \"Lorem ipsum do lor sit amet.\"};\n\n  REQUIRE(expected_str_part_2.size() > emio::default_cache_size);\n  REQUIRE(expected_str_part_3.size() > emio::default_cache_size);\n\n  std::string storage;\n  storage.resize(5 * emio::default_cache_size);\n  emio::iterator_buffer buf{storage.begin()};\n\n  emio::writer writer{buf};\n  CHECK(writer.write_char_n('x', expected_str_part_1.size()));\n  CHECK(writer.write_str(expected_str_part_2));\n  CHECK(writer.write_str_escaped(expected_str_part_3));\n\n  auto end = buf.out();\n  std::string s{storage.begin(), end};\n\n  CHECK(s == expected_str_part_1 + expected_str_part_2 + '\"' + expected_str_part_3 + '\"');\n}\n\nTEST_CASE(\"writer with cached buffer - write_char_escaped\", \"[writer]\") {\n  // Test strategy:\n  // * Construct a writer from a cached buffer.\n  // * Write an escaped char but only provide different sizes of chunks from the buffer.\n  // Expected: The escape method (same as write_str_escaped()) works as expected.\n\n  constexpr char char_to_escape = '\\n';\n  constexpr std::string_view expected_str = \"'\\\\n'\";\n\n  buffer_from_cb buf;\n  buf.set_cache_cb([]() -> std::span<char> {\n    return {};\n  });\n\n  emio::writer writer{buf};\n  CHECK(writer.write_char('c') == emio::err::eof);\n\n  size_t buffer_calls_cnt = 0;\n  std::vector<char> chunks;\n\n  SECTION(\"provide one char per chunk\") {\n    buf.set_cache_cb([&]() -> std::span<char> {\n      buffer_calls_cnt += 1;\n      chunks.push_back('\\0');\n      return {&chunks.back(), 1};\n    });\n    CHECK(writer.write_char_escaped(char_to_escape));\n    CHECK(buffer_calls_cnt == 4);\n    REQUIRE(chunks.size() == 4);\n  }\n  SECTION(\"provide two chars per chunk\") {\n    buf.set_cache_cb([&]() -> std::span<char> {\n      buffer_calls_cnt += 1;\n      chunks.push_back('\\0');\n      chunks.push_back('\\0');\n      return {&chunks.back() - 1, 2};\n    });\n    CHECK(writer.write_char_escaped(char_to_escape));\n    CHECK(buffer_calls_cnt == 2);\n    REQUIRE(chunks.size() == 4);\n  }\n  SECTION(\"provide three chars per chunk\") {\n    buf.set_cache_cb([&]() -> std::span<char> {\n      buffer_calls_cnt += 1;\n      chunks.push_back('\\0');\n      chunks.push_back('\\0');\n      chunks.push_back('\\0');\n      return {&chunks.back() - 2, 3};\n    });\n    CHECK(writer.write_char_escaped(char_to_escape));\n    CHECK(buffer_calls_cnt == 2);\n    REQUIRE(chunks.size() == 6);\n  }\n  SECTION(\"provide four chars per chunk\") {\n    buf.set_cache_cb([&]() -> std::span<char> {\n      buffer_calls_cnt += 1;\n      chunks.push_back('\\0');\n      chunks.push_back('\\0');\n      chunks.push_back('\\0');\n      chunks.push_back('\\0');\n      return {&chunks.back() - 3, 4};\n    });\n    CHECK(writer.write_char_escaped('\\n'));\n    CHECK(buffer_calls_cnt == 1);\n    REQUIRE(chunks.size() == 4);\n  }\n  // Check written result.\n  REQUIRE(chunks.size() >= 4);\n  CHECK(std::string_view{chunks.data(), 4} == expected_str);\n}\n\nTEST_CASE(\"writer over zero buffer\", \"[writer]\") {\n  // Test strategy:\n  // * Construct a writer from a zero span buffer.\n  // * Write data with the writer into the buffer.\n  // Expected: Every method fails with eof because of zero capacity.\n\n  constexpr emio::err eof = emio::err::eof;\n\n  emio::span_buffer buf{};\n  emio::writer writer{buf};\n\n  CHECK(writer.write_char('a') == eof);\n  CHECK(writer.write_char_n('a', 1) == eof);\n  CHECK(writer.write_char_escaped('a') == eof);\n  CHECK(writer.write_str(\"a\") == eof);\n  CHECK(writer.write_str_escaped(\"a\") == eof);\n  CHECK(writer.write_int(0) == eof);\n}\n"
  }
]