Showing preview only (616K chars total). Download the full file or copy to clipboard to get everything.
Repository: Viatorus/emio
Branch: main
Commit: 5d79a14d2969
Files: 147
Total size: 575.3 KB
Directory structure:
gitextract_uxa4xgnn/
├── .clang-format
├── .clang-tidy
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── pages.yml
├── .gitignore
├── CMakeLists.txt
├── CMakePresets.json
├── LICENSE
├── README.md
├── cmake/
│ ├── arm-none-eabi-toolchain.cmake
│ ├── coverage.cmake
│ ├── dev-mode.cmake
│ ├── elf_to_size_coverage.py
│ ├── folders.cmake
│ ├── install-config.cmake
│ ├── install-rules.cmake
│ ├── lint-targets.cmake
│ ├── lint.cmake
│ ├── prelude.cmake
│ ├── project-is-top-level.cmake
│ ├── size-coverage.cmake
│ └── variables.cmake
├── docs/
│ ├── API.md
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── DESIGN.md
│ ├── DEVELOPING.md
│ └── res/
│ └── class_diagram.puml
├── include/
│ └── emio/
│ ├── buffer.hpp
│ ├── detail/
│ │ ├── args.hpp
│ │ ├── bignum.hpp
│ │ ├── bitset.hpp
│ │ ├── conversion.hpp
│ │ ├── ct_vector.hpp
│ │ ├── format/
│ │ │ ├── args.hpp
│ │ │ ├── decode.hpp
│ │ │ ├── dragon.hpp
│ │ │ ├── format_to.hpp
│ │ │ ├── formatter.hpp
│ │ │ ├── parser.hpp
│ │ │ ├── ranges.hpp
│ │ │ └── specs.hpp
│ │ ├── misc.hpp
│ │ ├── parser.hpp
│ │ ├── predef.hpp
│ │ ├── scan/
│ │ │ ├── args.hpp
│ │ │ ├── parser.hpp
│ │ │ ├── scan_from.hpp
│ │ │ ├── scanner.hpp
│ │ │ └── specs.hpp
│ │ ├── utf.hpp
│ │ ├── validated_string.hpp
│ │ └── validated_string_storage.hpp
│ ├── emio.hpp
│ ├── format.hpp
│ ├── formatter.hpp
│ ├── iterator.hpp
│ ├── ranges.hpp
│ ├── reader.hpp
│ ├── result.hpp
│ ├── scan.hpp
│ ├── scanner.hpp
│ ├── std.hpp
│ └── writer.hpp
└── test/
├── benchmark/
│ ├── CMakeLists.txt
│ ├── bench_format.cpp
│ └── bench_scan.cpp
├── compile_test/
│ ├── CMakeLists.txt
│ └── compile.cpp
├── fuzzy/
│ └── dragon4/
│ ├── .gitignore
│ ├── Makefile
│ ├── main.cpp
│ ├── rust_ref/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── seeds/
│ └── test1
├── size_test/
│ ├── CMakeLists.txt
│ ├── base.cpp
│ ├── emio/
│ │ ├── doFormat_a.cpp
│ │ ├── format_all.cpp
│ │ ├── format_all_and_extra.cpp
│ │ ├── format_and_scan_all.cpp
│ │ ├── format_and_scan_all_runtime.cpp
│ │ ├── format_and_write_int.cpp
│ │ ├── format_double.cpp
│ │ ├── format_int.cpp
│ │ ├── format_int_twice.cpp
│ │ ├── format_runtime.cpp
│ │ ├── format_to.cpp
│ │ ├── format_to_n.cpp
│ │ ├── scan_all.cpp
│ │ ├── scan_int.cpp
│ │ ├── vformat.cpp
│ │ └── write_int.cpp
│ ├── fmt/
│ │ ├── doFormat_a.cpp
│ │ ├── fmt_dragon.cpp
│ │ ├── fmt_grisu.cpp
│ │ ├── fmt_grisu_and_dragon.cpp
│ │ ├── format_all.cpp
│ │ ├── format_all_and_extra.cpp
│ │ ├── format_int.cpp
│ │ ├── format_int_twice.cpp
│ │ ├── format_runtime.cpp
│ │ ├── format_to.cpp
│ │ ├── format_to_n.cpp
│ │ └── vformat.cpp
│ ├── std/
│ │ ├── locale.cpp
│ │ ├── snprintf.cpp
│ │ ├── snprintf_and_sscanf.cpp
│ │ ├── sscanf.cpp
│ │ ├── string.cpp
│ │ ├── string_stream.cpp
│ │ ├── to_chars.cpp
│ │ ├── to_string_double.cpp
│ │ └── to_string_int.cpp
│ └── stubs.cpp
├── static_analysis/
│ ├── CMakeLists.txt
│ └── test_main.cpp
└── unit_test/
├── CMakeLists.txt
├── detail/
│ ├── test_bignum.cpp
│ ├── test_bitset.cpp
│ ├── test_conversion.cpp
│ ├── test_ct_vector.cpp
│ ├── test_decode.cpp
│ ├── test_dragon.cpp
│ └── test_utf.cpp
├── integer_ranges.hpp
├── test_buffer.cpp
├── test_dynamic_format_spec.cpp
├── test_format.cpp
├── test_format_api.cpp
├── test_format_as.cpp
├── test_format_could_fail_api.cpp
├── test_format_emio_vs_fmt.cpp
├── test_format_string.cpp
├── test_format_to_api.cpp
├── test_format_to_n_api.cpp
├── test_formatted_size.cpp
├── test_formatter.cpp
├── test_iterator.cpp
├── test_print.cpp
├── test_ranges.cpp
├── test_reader.cpp
├── test_result.cpp
├── test_scan.cpp
├── test_std.cpp
└── test_writer.cpp
================================================
FILE CONTENTS
================================================
================================================
FILE: .clang-format
================================================
# Run manually to reformat a file:
# clang-format -i --style=file <file>
Language: Cpp
BasedOnStyle: Google
IndentPPDirectives: AfterHash
IndentCaseLabels: false
DerivePointerAlignment: false
Standard: c++20
ColumnLimit: 120
AllowShortFunctionsOnASingleLine: Empty
AllowShortLambdasOnASingleLine: None
IndentRequiresClause: true
================================================
FILE: .clang-tidy
================================================
---
# Enable ALL the things! Except not really
# cppcoreguidelines-avoid-magic-numbers/readability-magic-numbers: covers to many simply cases
# modernize-use-trailing-return-type: purely stylistic suggestion
# readability-identifier-length: not needed
# bugprone-easily-swappable-parameters: covers to many simple cases
# cppcoreguidelines-macro-usage: reduced to a minimum
# bugprone-macro-parentheses: false positive
# readability-function-cognitive-complexity: multiple if/else branches necessary to cover all use cases
# cppcoreguidelines-non-private-member-variables-in-classes: same as misc-non-private-member-variables-in-classes
# misc-non-private-member-variables-in-classes: allowed since classes are intended for single threading
# cppcoreguidelines-pro-type-union-access: allowed since it is used because of performance and compile-time reasons
# readability-else-after-return: allowed since if no else compile-time flow check fails and no optimization in debug
# bugprone-exception-escape: to many false positives
# cppcoreguidelines-pro-bounds-pointer-arithmetic: performance in debug
# cppcoreguidelines-pro-bounds-constant-array-index:: performance
Checks: >
bugprone-*,
concurrency-*,
cppcoreguidelines-*,
clang-analyzer-*,
misc-*,
modernize-*,
performance-*,
portability-*,
readability-*,
-clang-analyzer-osx*,
-clang-analyzer-llvm*,
-clang-analyzer-optin*,
-clang-analyzer-unix*,
-clang-analyzer-valist*,
-clang-diagnostic-ignored-optimization-argument,
-cppcoreguidelines-avoid-magic-numbers,
-readability-magic-numbers,
-modernize-use-trailing-return-type,
-readability-identifier-length,
-bugprone-easily-swappable-parameters,
-cppcoreguidelines-macro-usage,
-bugprone-macro-parentheses,
-readability-function-cognitive-complexity,
-cppcoreguidelines-non-private-member-variables-in-classes,
-misc-non-private-member-variables-in-classes,
-cppcoreguidelines-pro-type-union-access,
-readability-else-after-return,
-bugprone-exception-escape,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-misc-include-cleaner,
WarningsAsErrors: ''
CheckOptions:
- key: readability-identifier-naming.AbstractClassCase
value: 'lower_case'
- key: readability-identifier-naming.ClassCase
value: 'lower_case'
- key: readability-identifier-naming.ClassConstantCase
value: 'lower_case'
- key: readability-identifier-naming.ClassMemberCase
value: 'lower_case'
- key: readability-identifier-naming.ClassMethodCase
value: 'lower_case'
- key: readability-identifier-naming.ConstantCase
value: 'lower_case'
- key: readability-identifier-naming.ConstantParameterCase
value: 'lower_case'
- key: readability-identifier-naming.ConstantPointerParameterCase
value: 'lower_case'
- key: readability-identifier-naming.ConstexprFunctionCase
value: 'lower_case'
- key: readability-identifier-naming.ConstexprMethodCase
value: 'lower_case'
- key: readability-identifier-naming.ConstexprVariableCase
value: 'lower_case'
- key: readability-identifier-naming.EnumCase
value: 'lower_case'
- key: readability-identifier-naming.EnumConstantCase
value: 'lower_case'
- key: readability-identifier-naming.FunctionCase
value: 'lower_case'
- key: readability-identifier-naming.GlobalConstantCase
value: 'lower_case'
- key: readability-identifier-naming.GlobalConstantPointerCase
value: 'lower_case'
- key: readability-identifier-naming.GlobalFunctionCase
value: 'lower_case'
- key: readability-identifier-naming.GlobalPointerCase
value: 'lower_case'
- key: readability-identifier-naming.GlobalVariableCase
value: 'lower_case'
- key: readability-identifier-naming.InlineNamespaceCase
value: 'lower_case'
- key: readability-identifier-naming.LocalConstantCase
value: 'lower_case'
- key: readability-identifier-naming.LocalConstantPointerCase
value: 'lower_case'
- key: readability-identifier-naming.LocalPointerCase
value: 'lower_case'
- key: readability-identifier-naming.LocalVariableCase
value: 'lower_case'
- key: readability-identifier-naming.MacroDefinitionCase
value: 'UPPER_CASE'
- key: readability-identifier-naming.MemberCase
value: 'lower_case'
- key: readability-identifier-naming.MethodCase
value: 'lower_case'
- key: readability-identifier-naming.NamespaceCase
value: 'lower_case'
- key: readability-identifier-naming.ParameterCase
value: 'lower_case'
- key: readability-identifier-naming.ParameterPackCase
value: 'lower_case'
- key: readability-identifier-naming.PointerParameterCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMemberCase
value: 'lower_case'
- key: readability-identifier-naming.PrivateMemberSuffix
value: '_'
- key: readability-identifier-naming.PrivateMethodCase
value: 'lower_case'
- key: readability-identifier-naming.ProtectedMemberCase
value: 'lower_case'
- key: readability-identifier-naming.ProtectedMemberSuffix
value: '_'
- key: readability-identifier-naming.ProtectedMethodCase
value: 'lower_case'
- key: readability-identifier-naming.PublicMemberCase
value: 'lower_case'
- key: readability-identifier-naming.PublicMethodCase
value: 'lower_case'
- key: readability-identifier-naming.ScopedEnumConstantCase
value: 'lower_case'
- key: readability-identifier-naming.StaticConstantCase
value: 'lower_case'
- key: readability-identifier-naming.StaticVariableCase
value: 'lower_case'
- key: readability-identifier-naming.StructCase
value: 'lower_case'
- key: readability-identifier-naming.TemplateParameterCase
value: 'CamelCase'
- key: readability-identifier-naming.TypeTemplateParameterIgnoredRegexp
value: 'expr-type' # see https://github.com/llvm/llvm-project/issues/46097
- key: readability-identifier-naming.TemplateTemplateParameterCase
value: 'CamelCase'
- key: readability-identifier-naming.TypeAliasCase
value: 'lower_case'
- key: readability-identifier-naming.TypedefCase
value: 'lower_case'
- key: readability-identifier-naming.TypeTemplateParameterCase
value: 'CamelCase'
- key: readability-identifier-naming.UnionCase
value: 'lower_case'
- key: readability-identifier-naming.ValueTemplateParameterCase
value: 'CamelCase'
- key: readability-identifier-naming.VariableCase
value: 'lower_case'
- key: readability-identifier-naming.VirtualMethodCase
value: 'lower_case'
...
================================================
FILE: .github/workflows/ci.yml
================================================
name: Continuous Integration
on:
push:
branches:
- main
pull_request:
jobs:
checks:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Install GCC
run: |
sudo apt update && sudo apt install -y gcc-11 g++-11
echo "CC=gcc-11" >> $GITHUB_ENV
echo "CXX=g++-11" >> $GITHUB_ENV
- name: Install cppcheck
run: |
sudo apt update && sudo apt install -y cppcheck
cppcheck --version
- name: Install clang-format and clang-tidy
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main'
sudo apt update && sudo apt install -y clang-format-17 clang-tidy-17
sudo update-alternatives --remove-all clang-format || true
sudo update-alternatives --remove-all clang-tidy || true
sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-17 1000
sudo update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-17 1000
clang-tidy --version
clang-format --version
- name: Run clang-format
run: cmake -D FORMAT_COMMAND=clang-format -P cmake/lint.cmake
- name: Run cppcheck and clang-tidy
# Cppcheck's checks are disabled. They are mostly redundant to clang-tidy and there are too many false positives.
# But the parsing of cppcheck is kept to be compatible with its compiler frontend.
if: ${{ !cancelled() }}
run: |
cmake --preset=ci-checks
cmake --build build/test/static_analysis -j $(nproc)
unit-tests:
strategy:
matrix:
env: [ {os: ubuntu-22.04, compiler: gcc-11, std: 20},
{os: ubuntu-22.04, compiler: gcc-12, std: 20},
{os: ubuntu-22.04, compiler: gcc-12, std: 23},
{os: ubuntu-24.04, compiler: gcc-13, std: 20},
{os: ubuntu-24.04, compiler: gcc-13, std: 23},
{os: ubuntu-24.04, compiler: gcc-14, std: 20},
{os: ubuntu-24.04, compiler: gcc-14, std: 23},
{os: ubuntu-22.04, compiler: clang-16, std: 20},
{os: ubuntu-22.04, compiler: clang-16, std: 23},
{os: ubuntu-22.04, compiler: clang-17, std: 20},
{os: ubuntu-22.04, compiler: clang-17, std: 23},
{os: ubuntu-24.04, compiler: clang-18, std: 20},
{os: ubuntu-24.04, compiler: clang-18, std: 23},
{os: ubuntu-24.04, compiler: clang-19, std: 20},
{os: ubuntu-24.04, compiler: clang-19, std: 23}
]
build_type: [ Debug, Release ]
runs-on: ${{ matrix.env.os }}
steps:
- uses: actions/checkout@v3
- name: Install Compiler
run: |
if [[ "$compiler" == "gcc-11" ]]; then
sudo apt update && sudo apt install -y gcc-11 g++-11
echo "CC=gcc-11" >> $GITHUB_ENV
echo "CXX=g++-11" >> $GITHUB_ENV
elif [[ "$compiler" == "gcc-12" ]]; then
sudo apt update && sudo apt install -y gcc-12 g++-12
echo "CC=gcc-12" >> $GITHUB_ENV
echo "CXX=g++-12" >> $GITHUB_ENV
elif [[ "$compiler" == "gcc-13" ]]; then
sudo apt update && sudo apt install -y gcc-13 g++-13
echo "CC=gcc-13" >> $GITHUB_ENV
echo "CXX=g++-13" >> $GITHUB_ENV
elif [[ "$compiler" == "clang-16" ]]; then
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main'
sudo apt update && sudo apt install -y clang-16
echo "CC=clang-16" >> $GITHUB_ENV
echo "CXX=clang++-16" >> $GITHUB_ENV
elif [[ "$compiler" == "clang-17" ]]; then
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main'
sudo apt update && sudo apt install -y clang-17
echo "CC=clang-17" >> $GITHUB_ENV
echo "CXX=clang++-17" >> $GITHUB_ENV
elif [[ "$compiler" == "clang-18" ]]; then
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-18 main'
sudo apt update && sudo apt install -y clang-18
echo "CC=clang-18" >> $GITHUB_ENV
echo "CXX=clang++-18" >> $GITHUB_ENV
elif [[ "$compiler" == "clang-20" ]]; then
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo add-apt-repository 'deb http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main'
sudo apt update && sudo apt install -y clang-20
echo "CC=clang-20" >> $GITHUB_ENV
echo "CXX=clang++-20" >> $GITHUB_ENV
fi
env:
compiler: ${{ matrix.env.compiler }}
- name: Configure
run: cmake --preset=ci-ubuntu -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_CXX_STANDARD=${{ matrix.env.std }}
- name: Build Catch2
run: cmake --build build -t Catch2WithMain -j $(nproc)
- name: Build
run: cmake --build build -j $(nproc)
- name: Install
run: cmake --install build --prefix prefix
- name: Test
working-directory: build
run: ctest --verbose --output-on-failure
coverage:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Install GCC
run: |
sudo apt update && sudo apt install -y gcc-11 g++-11
echo "CC=gcc-11" >> $GITHUB_ENV
echo "CXX=g++-11" >> $GITHUB_ENV
- name: Install LCov
run: sudo apt update && sudo apt install lcov -q -y
- name: Configure
run: cmake --preset=ci-coverage
- name: Build Catch2
run: cmake --build build -t Catch2WithMain -j $(nproc)
- name: Build
run: cmake --build build/test/unit_test -j $(nproc)
- name: Test
working-directory: build/test/unit_test
run: ctest --output-on-failure
- name: Process coverage info
run: cmake --build build -t coverage
- name: Submit to codecov.io
uses: codecov/codecov-action@v3
with:
files: build/coverage.info
memcheck:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- name: Install GCC
run: |
sudo apt update && sudo apt install -y gcc-14 g++-14
echo "CC=gcc-14" >> $GITHUB_ENV
echo "CXX=g++-14" >> $GITHUB_ENV
- name: Install valgrind
run: sudo apt update && sudo apt install -y valgrind
- name: Configure
run: cmake --preset=ci-memcheck
- name: Build Catch2
run: cmake --build build -t Catch2WithMain -j $(nproc)
- name: Build
run: cmake --build build -j $(nproc)
- name: Install
run: cmake --install build --prefix prefix
- name: Test
working-directory: build
run: |
if ! ctest --verbose --output-on-failure -T memcheck; then
find Testing/Temporary -name "MemoryChecker.*.log" -exec cat {} +
exit 1
fi
sanitize:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- name: Install GCC
run: |
sudo apt update && sudo apt install -y gcc-14 g++-14
echo "CC=gcc-14" >> $GITHUB_ENV
echo "CXX=g++-14" >> $GITHUB_ENV
- name: Configure
run: cmake --preset=ci-sanitize
- name: Build Catch2
run: cmake --build build -t Catch2WithMain -j $(nproc)
- name: Build
run: cmake --build build/test/unit_test -j $(nproc)
- name: Test
working-directory: build/test/unit_test
env:
ASAN_OPTIONS: "strict_string_checks=1:\
detect_stack_use_after_return=1:\
check_initialization_order=1:\
strict_init_order=1:\
detect_leaks=1"
UBSAN_OPTIONS: print_stacktrace=1
run: ctest --output-on-failure
size-test:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Install GCC
run: |
sudo apt update && sudo apt install -y gcc-11 g++-11
echo "CC=gcc-11" >> $GITHUB_ENV
echo "CXX=g++-11" >> $GITHUB_ENV
- name: Configure
run: cmake --preset=ci-size-coverage -DCMAKE_BUILD_TYPE=MinSizeRel
- name: Build
run: cmake --build build/test/size_test -j $(nproc)
- name: Size-Coverage
run: cmake --build build -t size-coverage
size-test-embedded:
strategy:
matrix:
preset: [ ci-size-coverage-embedded, ci-size-coverage-embedded-nano ]
runs-on: ubuntu-22.04
env:
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
steps:
- uses: actions/checkout@v3
- name: Cache toolchain
id: cache-toolchain
uses: actions/cache@v4
with:
path: arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi
key: ${{ env.DOWNLOAD_LINK }}
- name: Install GCC ARM none eabi
if: steps.cache-toolchain.outputs.cache-hit != 'true'
run: |
curl -L $DOWNLOAD_LINK | tar xJ
- name: Configure
run: cmake --preset=${{ matrix.preset }} -DCMAKE_BUILD_TYPE=MinSizeRel
- name: Build
run: cmake --build build/test/size_test -j $(nproc)
- name: Size-Coverage
run: cmake --build build -t size-coverage
# - name: Coveralls - Doesn't work.
# uses: coverallsapp/github-action@master
# with:
# github-token: ${{ secrets.GITHUB_TOKEN }}
# path-to-lcov: build/test/size_test/size-coverage.info
================================================
FILE: .github/workflows/pages.yml
================================================
name: Deploy Github Pages
on:
push:
branches:
- main
tags:
- "[0-9]+.[0-9]+.[0-9]+"
jobs:
deploy-pages:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Generate single header file
run: |
pip install quom
if [[ -z "$(git tag --points-at HEAD)" ]]; then
DST=trunk
else
DST=$(git tag)
fi
mkdir -p web/dist/$DST
quom include/emio/emio.hpp web/dist/$DST/emio.hpp
- name: Deploy
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: web/
clean: false
================================================
FILE: .gitignore
================================================
.idea/
.vs/
.vscode/
build/
cmake/open-cpp-coverage.cmake
cmake-build-*/
prefix/
CMakeLists.txt.user
CMakeUserPresets.json
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.14)
include(cmake/prelude.cmake)
project(
emio
VERSION 0.2.0
DESCRIPTION "Character input/output library for embedded systems."
HOMEPAGE_URL "https://github.com/viatorus/emio"
LANGUAGES CXX
)
include(cmake/project-is-top-level.cmake)
include(cmake/variables.cmake)
# ---- Declare library ----
add_library(emio_emio INTERFACE)
add_library(emio::emio ALIAS emio_emio)
set_property(
TARGET emio_emio PROPERTY
EXPORT_NAME emio
)
target_include_directories(
emio_emio ${warning_guard}
INTERFACE
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
)
target_compile_features(emio_emio INTERFACE cxx_std_20)
# ---- Install rules ----
if (NOT CMAKE_SKIP_INSTALL_RULES)
include(cmake/install-rules.cmake)
endif ()
# ---- Developer mode ----
if (NOT emio_DEVELOPER_MODE)
return()
elseif (NOT PROJECT_IS_TOP_LEVEL)
message(
AUTHOR_WARNING
"Developer mode is intended for developers of emio"
)
endif ()
include(cmake/dev-mode.cmake)
================================================
FILE: CMakePresets.json
================================================
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 14,
"patch": 0
},
"configurePresets": [
{
"name": "cmake-pedantic",
"hidden": true,
"warnings": {
"dev": true,
"deprecated": true,
"uninitialized": true,
"unusedCli": true,
"systemVars": false
},
"errors": {
"dev": true,
"deprecated": true
}
},
{
"name": "dev-mode",
"hidden": true,
"inherits": "cmake-pedantic",
"cacheVariables": {
"emio_DEVELOPER_MODE": "ON"
}
},
{
"name": "cppcheck",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_CPPCHECK": "cppcheck;--error-exitcode=13"
}
},
{
"name": "clang-tidy",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_CLANG_TIDY": "clang-tidy;--header-filter=${sourceDir}/*;-warnings-as-errors=*"
}
},
{
"name": "valgrind",
"hidden": true,
"cacheVariables": {
"MEMORYCHECK_COMMAND_OPTIONS": "--error-exitcode=1 --leak-check=full"
}
},
{
"name": "ci-std",
"description": "This preset makes sure the project actually builds with at least the specified standard",
"hidden": true,
"cacheVariables": {
"CMAKE_CXX_EXTENSIONS": "OFF",
"CMAKE_CXX_STANDARD": "23",
"CMAKE_CXX_STANDARD_REQUIRED": "ON"
}
},
{
"name": "flags-unix",
"hidden": true,
"environment": {
"CXX_FLAGS": "-Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wcast-qual -Wshadow -Wformat=2 -Wundef"
}
},
{
"name": "ci-unix",
"generator": "Unix Makefiles",
"hidden": true,
"inherits": [
"flags-unix",
"ci-std"
],
"cacheVariables": {
"CMAKE_CXX_FLAGS": "$env{CXX_FLAGS}",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "ci-arm-none-eabi",
"generator": "Unix Makefiles",
"hidden": true,
"inherits": [
"flags-unix",
"ci-std"
],
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/cmake/arm-none-eabi-toolchain.cmake",
"CMAKE_CXX_FLAGS": "$env{CXX_FLAGS} -ffunction-sections -fdata-sections -mcpu=cortex-m7 -mthumb -g0 -fno-exceptions -fno-rtti",
"CMAKE_EXE_LINKER_FLAGS": "-mcpu=cortex-m7 -mthumb -Wl,--gc-sections",
"CMAKE_BUILD_TYPE": "MinSizeRel"
}
},
{
"name": "ci-arm-none-eabi-nano",
"generator": "Unix Makefiles",
"hidden": true,
"inherits": [
"ci-arm-none-eabi"
],
"cacheVariables": {
"CMAKE_EXE_LINKER_FLAGS": "-mcpu=cortex-m7 -mthumb -specs=nano.specs -Wl,--gc-sections"
}
},
{
"name": "coverage-unix",
"inherits": [
"ci-build",
"ci-unix"
],
"hidden": true,
"cacheVariables": {
"ENABLE_COVERAGE": "ON",
"CMAKE_BUILD_TYPE": "Coverage",
"CMAKE_CXX_FLAGS_COVERAGE": "-O0 -g --coverage",
"CMAKE_EXE_LINKER_FLAGS_COVERAGE": "--coverage",
"CMAKE_SHARED_LINKER_FLAGS_COVERAGE": "--coverage"
}
},
{
"name": "ci-coverage",
"inherits": [
"coverage-unix",
"dev-mode"
],
"cacheVariables": {
"COVERAGE_HTML_COMMAND": ""
}
},
{
"name": "ci-sanitize",
"inherits": [
"ci-build",
"ci-unix",
"dev-mode"
],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Sanitize",
"CMAKE_CXX_FLAGS_SANITIZE": "-O2 -g -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common"
}
},
{
"name": "ci-build",
"binaryDir": "${sourceDir}/build",
"hidden": true
},
{
"name": "ci-ubuntu",
"inherits": [
"ci-build",
"ci-unix",
"dev-mode"
]
},
{
"name": "ci-memcheck",
"inherits": [
"ci-build",
"ci-unix",
"valgrind",
"dev-mode"
]
},
{
"name": "ci-checks",
"inherits": [
"ci-build",
"ci-unix",
"clang-tidy",
"cppcheck",
"dev-mode"
]
},
{
"name": "ci-embedded",
"inherits": [
"ci-build",
"ci-arm-none-eabi",
"dev-mode"
]
},
{
"name": "ci-size-coverage",
"inherits": [
"ci-build",
"ci-unix",
"dev-mode"
],
"cacheVariables": {
"CMAKE_CXX_FLAGS": "$env{CXX_FLAGS} -ffunction-sections -fdata-sections -g0 -fno-exceptions -fno-rtti -fno-asynchronous-unwind-tables",
"BUILD_SIZE_COVERAGE": "ON",
"ENABLE_SIZE_COVERAGE": "ON",
"SIZE_COVERAGE_HTML_COMMAND": "",
"SIZE_TOOL": "size"
}
},
{
"name": "ci-size-coverage-embedded",
"inherits": [
"ci-build",
"ci-arm-none-eabi",
"dev-mode"
],
"cacheVariables": {
"BUILD_SIZE_COVERAGE": "ON",
"ENABLE_SIZE_COVERAGE": "ON",
"SIZE_COVERAGE_HTML_COMMAND": "",
"SIZE_TOOL": "arm-none-eabi-size"
}
},
{
"name": "ci-size-coverage-embedded-nano",
"inherits": [
"ci-build",
"ci-arm-none-eabi-nano",
"dev-mode"
],
"cacheVariables": {
"BUILD_SIZE_COVERAGE": "ON",
"ENABLE_SIZE_COVERAGE": "ON",
"SIZE_COVERAGE_HTML_COMMAND": "",
"SIZE_TOOL": "arm-none-eabi-size"
}
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "ci-ubuntu"
}
]
}
================================================
FILE: LICENSE
================================================
Copyright (c) 2021 - present, Toni Neubert (viatorus/emio)
Copyright (c) 2012 - present, Victor Zverovich (fmtlib/fmt)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================

[](https://github.com/Viatorus/emio/actions/workflows/ci.yml)
[](https://codecov.io/gh/Viatorus/emio)
[](https://conan.io/center/recipes/emio)
**em{io}** is a safe and fast high-level and low-level character input/output library for bare-metal and RTOS based
embedded systems with a very small binary footprint.
```cpp
// High-level
std::string str = emio::format("The answer is {}.", 42); // Format argument.
int answer{};
emio::result<void> scan_res = emio::scan(str, "The answer is {}.", answer); // Scan input string.
if (scan_res) {
emio::print("The answer is {}.", answer); // Output to console.
}
// Without using heap.
emio::static_buffer<128> buf{};
emio::format_to(buf, "The answer is {:#x}.", 42).value();
buf.view(); // <- The answer is 0x2a.
// Low-level
emio::writer wrt{buf};
wrt.write_str(" In decimal: ").value();
wrt.write_int(42).value();
wrt.write_char('.').value();
buf.view(); // <- The answer is 0x2a. In decimal: 42.
emio::reader rdr{"17c"};
EMIO_TRY(uint32_t number, rdr.parse_int<uint32_t>()); // <- 17
EMIO_TRY(char suffix, rdr.read_char()); // <- c
```
[**This library is in beta status! Please help to make it fly!**](https://github.com/Viatorus/emio/milestone/1)
* [API documentation](docs/API.md)
* Try emio [online](https://godbolt.org/z/fP7z7MzbG).
## Yet another character input/output library
Bare-metal and RTOS based embedded systems do have special requirements which are mostly overlooked by the C++ standard,
its implementations and other libraries.
Therefore, this library:
* has a very small binary footprint **(~38 times smaller than fmtlib!)**
* returns a result object instead of throwing an exception
* provides a high-level and low-level API which can be used at compile-time
Read more about it in the [DESIGN](docs/DESIGN.md) document.
## Including emio in your project
- With CMake and fetch content
```cmake
FetchContent_Declare(
emio
GIT_TAG main
GIT_REPOSITORY https://github.com/Viatorus/emio.git
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(emio)
```
- Download the [single header file](https://viatorus.github.io/emio/) generated with [Quom](https://github.com/Viatorus/quom)
- From [Conan Center](https://conan.io/center/recipes/emio)
A compiler supporting C++20 is required. Tested with GCC 11/12/13 and Clang 16/17.
## Contributing
See the [CONTRIBUTING](docs/CONTRIBUTING.md) document.
## Licensing
em{io} is distributed under the [MIT license](LICENSE).
================================================
FILE: cmake/arm-none-eabi-toolchain.cmake
================================================
cmake_minimum_required(VERSION 3.14.0)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # do not link executable when testing the compiler.
set(TOOLCHAIN_BIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi/bin)
set(CMAKE_C_COMPILER ${TOOLCHAIN_BIN_PATH}/arm-none-eabi-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN_PATH}/arm-none-eabi-g++)
set(CMAKE_AR ${TOOLCHAIN_BIN_PATH}/arm-none-eabi-gcc-ar)
set(CMAKE_RANLIB ${TOOLCHAIN_BIN_PATH}/arm-none-eabi-gcc-ranlib)
================================================
FILE: cmake/coverage.cmake
================================================
# ---- Variables ----
# We use variables separate from what CTest uses, because those have
# customization issues
set(
COVERAGE_TRACE_COMMAND
lcov -c -q
-o "${PROJECT_BINARY_DIR}/coverage.info"
-d "${PROJECT_BINARY_DIR}"
--include "${PROJECT_SOURCE_DIR}/*"
CACHE STRING
"; separated command to generate a trace for the 'coverage' target"
)
set(
COVERAGE_HTML_COMMAND
genhtml --legend -f -q
"${PROJECT_BINARY_DIR}/coverage.info"
-p "${PROJECT_SOURCE_DIR}"
-o "${PROJECT_BINARY_DIR}/coverage_html"
CACHE STRING
"; separated command to generate an HTML report for the 'coverage' target"
)
# ---- Coverage target ----
add_custom_target(
coverage
COMMAND ${COVERAGE_TRACE_COMMAND}
COMMAND ${COVERAGE_HTML_COMMAND}
COMMENT "Generating coverage report"
VERBATIM
)
================================================
FILE: cmake/dev-mode.cmake
================================================
include(cmake/folders.cmake)
set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1)
include(CTest)
if (BUILD_TESTING OR BUILD_SIZE_COVERAGE)
set(CMAKE_MODULE_PATH
CACHE INTERNAL
FORCE
)
set(FMT_HEADERS
CACHE INTERNAL
FORCE
)
set(FMT_OS OFF
CACHE INTERNAL
"Path to downloaded Catch2 modules"
FORCE
)
Include(FetchContent)
FetchContent_Declare(
fmt
GIT_TAG d9bc5f1320332db9a4bf7e103b0813b94e369304 # 9.1.1 - not released
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
)
FetchContent_MakeAvailable(fmt)
endif ()
if (BUILD_TESTING)
Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_TAG v3.4.0
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(Catch2)
endif ()
if (BUILD_TESTING)
add_subdirectory(test/benchmark)
add_subdirectory(test/compile_test)
add_subdirectory(test/static_analysis)
add_subdirectory(test/unit_test)
endif ()
if (BUILD_SIZE_COVERAGE)
add_subdirectory(test/size_test)
endif ()
option(ENABLE_COVERAGE "Enable coverage support separate from CTest's" OFF)
if (ENABLE_COVERAGE)
include(cmake/coverage.cmake)
endif ()
include(cmake/lint-targets.cmake)
add_folders(Project)
================================================
FILE: cmake/elf_to_size_coverage.py
================================================
#!/usr/bin/env python3
import argparse
import re
import subprocess
import sys
from io import StringIO
from pathlib import Path
def parse_args(args):
parser = argparse.ArgumentParser(
description='Creates the size difference of a base file size to multiple other files sizes as lcov info file')
parser.add_argument('--size-tool', type=Path, help='path to the GNU size tool', metavar='SIZE_TOOL', required=True)
parser.add_argument('base', type=Path, help='the first file is used as base size', metavar='BASE_FILE')
parser.add_argument('files', type=Path, nargs='+', help='file(s) to compare', metavar='FILE')
parser.add_argument('--output', '-o', type=Path, help='specify the output file', metavar='OUTPUT_FILE',
required=True)
return parser.parse_args(args)
class Generator:
def __init__(self, size_tool, base_file, out):
self._size_tool = size_tool
self._base_file = base_file.absolute()
self._base_size = self._get_size(base_file)
self._out = out
self._out.write("TN:\n")
print("{}: {} (base)".format(base_file, self._base_size))
def generate(self, file: Path):
file_path = file.absolute()
file_size = self._get_size(file)
print('{} - {}'.format(file_path, file_size))
if file_size < self._base_size:
raise Exception(f'{file} cannot be smaller than {self._base_file}')
self._out.write(f'SF:{file_path}\n')
for covered in range(self._base_size):
self._out.write(f'DA:{covered},1\n')
for uncovered in range(self._base_size, file_size):
self._out.write(f'DA:{uncovered},0\n')
self._out.write('end_of_record\n')
def _get_size(self, file: Path):
output = subprocess.check_output([str(self._size_tool), '--format=GNU', str(file)], encoding='UTF-8')
# text data bss total filename
# 348 120 28 496 base
# Get total from regex:
total = re.search('text\s+data\s+bss\s+total\s+filename\n\s+\d+\s+\d+\s+\d+\s+(\d+)\s+', output)
if not total:
raise Exception(f'Unexpected output from size: {output}')
return int(total[1])
def run():
args = parse_args(sys.argv[1:])
if not args.base.is_file():
raise Exception(f'Base file not found: {args.base}')
output = StringIO()
generator = Generator(args.size_tool, args.base, output)
for file in args.files:
if not file.is_file():
raise Exception(f'File not found: {file}')
generator.generate(file)
args.output.write_text(output.getvalue())
if __name__ == '__main__':
run()
================================================
FILE: cmake/folders.cmake
================================================
set_property(GLOBAL PROPERTY USE_FOLDERS YES)
# Call this function at the end of a directory scope to assign a folder to
# targets created in that directory. Utility targets will be assigned to the
# UtilityTargets folder, otherwise to the ${name}Targets folder. If a target
# already has a folder assigned, then that target will be skipped.
function(add_folders name)
get_property(targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
foreach (target IN LISTS targets)
get_property(folder TARGET "${target}" PROPERTY FOLDER)
if (DEFINED folder)
continue()
endif ()
set(folder Utility)
get_property(type TARGET "${target}" PROPERTY TYPE)
if (NOT type STREQUAL "UTILITY")
set(folder "${name}")
endif ()
set_property(TARGET "${target}" PROPERTY FOLDER "${folder}Targets")
endforeach ()
endfunction()
================================================
FILE: cmake/install-config.cmake
================================================
include("${CMAKE_CURRENT_LIST_DIR}/emioTargets.cmake")
================================================
FILE: cmake/install-rules.cmake
================================================
if (PROJECT_IS_TOP_LEVEL)
set(CMAKE_INSTALL_INCLUDEDIR include/emio CACHE PATH "")
endif ()
# Project is configured with no languages, so tell GNUInstallDirs the lib dir
set(CMAKE_INSTALL_LIBDIR lib CACHE PATH "")
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
# find_package(<package>) call for consumers to find this project
set(package emio)
install(
DIRECTORY include/
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT emio_Development
)
install(
TARGETS emio_emio
EXPORT emioTargets
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
write_basic_package_version_file(
"${package}ConfigVersion.cmake"
COMPATIBILITY SameMajorVersion
ARCH_INDEPENDENT
)
# Allow package maintainers to freely override the path for the configs
set(
emio_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/${package}"
CACHE PATH "CMake package config location relative to the install prefix"
)
mark_as_advanced(emio_INSTALL_CMAKEDIR)
install(
FILES cmake/install-config.cmake
DESTINATION "${emio_INSTALL_CMAKEDIR}"
RENAME "${package}Config.cmake"
COMPONENT emio_Development
)
install(
FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake"
DESTINATION "${emio_INSTALL_CMAKEDIR}"
COMPONENT emio_Development
)
install(
EXPORT emioTargets
NAMESPACE emio::
DESTINATION "${emio_INSTALL_CMAKEDIR}"
COMPONENT emio_Development
)
if (PROJECT_IS_TOP_LEVEL)
include(CPack)
endif ()
================================================
FILE: cmake/lint-targets.cmake
================================================
set(
FORMAT_PATTERNS
source/*.cpp source/*.hpp
include/*.hpp
test/*.cpp test/*.hpp
example/*.cpp example/*.hpp
CACHE STRING
"; separated patterns relative to the project source dir to format"
)
set(FORMAT_COMMAND clang-format CACHE STRING "Formatter to use")
add_custom_target(
format-check
COMMAND "${CMAKE_COMMAND}"
-D "FORMAT_COMMAND=${FORMAT_COMMAND}"
-D "PATTERNS=${FORMAT_PATTERNS}"
-P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
COMMENT "Linting the code"
VERBATIM
)
add_custom_target(
format-fix
COMMAND "${CMAKE_COMMAND}"
-D "FORMAT_COMMAND=${FORMAT_COMMAND}"
-D "PATTERNS=${FORMAT_PATTERNS}"
-D FIX=YES
-P "${PROJECT_SOURCE_DIR}/cmake/lint.cmake"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
COMMENT "Fixing the code"
VERBATIM
)
================================================
FILE: cmake/lint.cmake
================================================
cmake_minimum_required(VERSION 3.14)
macro(default name)
if (NOT DEFINED "${name}")
set("${name}" "${ARGN}")
endif ()
endmacro()
default(FORMAT_COMMAND clang-format)
default(
PATTERNS
source/*.cpp source/*.hpp
include/*.hpp
test/*.cpp test/*.hpp
example/*.cpp example/*.hpp
)
default(FIX NO)
set(flag --output-replacements-xml)
set(args OUTPUT_VARIABLE output)
if (FIX)
set(flag -i)
set(args "")
endif ()
file(GLOB_RECURSE files ${PATTERNS})
set(badly_formatted "")
set(output "")
string(LENGTH "${CMAKE_SOURCE_DIR}/" path_prefix_length)
foreach (file IN LISTS files)
execute_process(
COMMAND "${FORMAT_COMMAND}" --style=file "${flag}" "${file}"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
RESULT_VARIABLE result
${args}
)
if (NOT result EQUAL "0")
message(FATAL_ERROR "'${file}': formatter returned with ${result}")
endif ()
if (NOT FIX AND output MATCHES "\n<replacement offset")
string(SUBSTRING "${file}" "${path_prefix_length}" -1 relative_file)
list(APPEND badly_formatted "${relative_file}")
endif ()
set(output "")
endforeach ()
if (NOT badly_formatted STREQUAL "")
list(JOIN badly_formatted "\n" bad_list)
message("The following files are badly formatted:\n\n${bad_list}\n")
message(FATAL_ERROR "Run again with FIX=YES to fix these files.")
endif ()
================================================
FILE: cmake/prelude.cmake
================================================
# ---- In-source guard ----
if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(
FATAL_ERROR
"In-source builds are not supported. "
"Please read the BUILDING document before trying to build this project. "
"You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first."
)
endif ()
================================================
FILE: cmake/project-is-top-level.cmake
================================================
# This variable is set by project() in CMake 3.21+
string(
COMPARE EQUAL
"${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}"
PROJECT_IS_TOP_LEVEL
)
================================================
FILE: cmake/size-coverage.cmake
================================================
# ---- Variables ----cd
get_filename_component(COMPILER_DIR ${CMAKE_CXX_COMPILER} DIRECTORY)
# We use variables separate from what CTest uses, because those have
# customization issues
set(
SIZE_COVERAGE_TRACE_COMMAND
python3 ${CMAKE_SOURCE_DIR}/cmake/elf_to_size_coverage.py
--size-tool "${COMPILER_DIR}/${SIZE_TOOL}"
-o "${PROJECT_BINARY_DIR}/size-coverage.info"
${SIZE_COVERAGE_FILES}
CACHE STRING
"; separated command to generate a trace for the 'size-coverage' target"
)
set(
SIZE_COVERAGE_HTML_COMMAND
genhtml --legend -f -q
"${PROJECT_BINARY_DIR}/size-coverage.info"
-p "${PROJECT_SOURCE_DIR}"
-o "${PROJECT_BINARY_DIR}/size_coverage_html"
CACHE STRING
"; separated command to generate an HTML report for the 'size-coverage' target"
)
# ---- Coverage target ----
add_custom_target(
size-coverage
COMMAND ${SIZE_COVERAGE_TRACE_COMMAND}
COMMAND ${SIZE_COVERAGE_HTML_COMMAND}
COMMENT "Generating size-coverage report"
VERBATIM
)
================================================
FILE: cmake/variables.cmake
================================================
# ---- Developer mode ----
# Developer mode enables targets and code paths in the CMake scripts that are
# only relevant for the developer(s) of emio
# Targets necessary to build the project must be provided unconditionally, so
# consumers can trivially build and package the project
if (PROJECT_IS_TOP_LEVEL)
option(emio_DEVELOPER_MODE "Enable developer mode" OFF)
endif ()
# ---- Warning guard ----
# target_include_directories with the SYSTEM modifier will request the compiler
# to omit warnings from the provided paths, if the compiler supports that
# This is to provide a user experience similar to find_package when
# add_subdirectory or FetchContent is used to consume this project
set(warning_guard "")
if (NOT PROJECT_IS_TOP_LEVEL)
option(
emio_INCLUDES_WITH_SYSTEM
"Use SYSTEM modifier for emio's includes, disabling warnings"
ON
)
mark_as_advanced(emio_INCLUDES_WITH_SYSTEM)
if (emio_INCLUDES_WITH_SYSTEM)
set(warning_guard SYSTEM)
endif ()
endif ()
# Export compile commands for external tools.
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
================================================
FILE: docs/API.md
================================================
# API
This is a small API overview. The public API is fully documented inside the source code. Unless otherwise stated,
everything can be used at compile-time.
The public namespace is `emio` only - no deeper nesting.
* [err](#err)
* [result](#result)
* [Buffer](#buffer)
+ [memory_buffer](#memorybuffer)
+ [span_buffer](#spanbuffer)
+ [static_buffer](#staticbuffer)
+ [iterator_buffer](#iteratorbuffer)
+ [file_buffer](#filebuffer)
+ [truncating_buffer](#truncatingbuffer)
* [Reader](#reader)
* [Writer](#writer)
* [Format](#format)
+ [Dynamic format specification](#dynamic-format-specification)
+ [Formatter](#formatter)
* [Print](#print)
* [Scan](#scan)
+ [Scanner](#scanner)
## err
A list of possible I/O errors as enum.
Every function describes the possible errors which can occur. See the source code documentation for more information.
`eof`
- End of file (e.g. reaching the end of an output array).
`invalid_argument`
- A parameter is incorrect (e.g. the output base is invalid).
`invalid_data`
- The data is malformed (e.g. no digit where a digit was expected).
`out_of_range`
- The parsed value is not in the range representable by the type (e.g. parsing 578 as uint8_t).
`invalid_format`
- The format string is invalid (e.g. missing arguments, wrong syntax).
`to_string(err) -> string_view`
- Returns the name of the error code.
*Example*
```cpp
std::string_view error_msg = emio::to_string(emio::err::invalid_format); // invalid_format
```
## result
`template <typename T> class result;`
- The return type of almost all functions to propagate a value of type `T` on success or an error of type `emio::err`
on failure.
*constructor(arg)*
- Constructable either from T or `emio::err`.
`has_value() -> bool`
- Checks whether the object holds a value.
`has_error() -> bool`
- Checks whether the object holds an error.
`value() -> T`
- Returns the value or throws/terminates if no value is held.
`value_or(T) -> T`
- Returns the value or returns the passed alternative if no value is held.
`assume_value() -> T`
- Returns the value without any checks. Invokes undefined behavior if no value is held.
`error() -> emio::err`
- Returns the error or throws/terminates if no error is held.
`assume_error() -> emio::err`
- Returns the error without any checks. Invokes undefined behavior if no error is held.
There exists two helper macros to simplify the control flow:
`EMIO_TRYV(expr)`
- Evaluates an expression *expr*. If successful, continues the execution. If unsuccessful, immediately returns from the
calling function.
`EMIO_TRY(var, expr)`
- Evaluates an expression *expr*. If successful, assigns the value to a declaration *var*. If unsuccessful, immediately
returns from the calling function.
*Example*
```cpp
result<int> parse_one(std::string_view sv) {
if (sv.empty()) {
return emio::err::eof;
}
if (sv == "1") {
return 1;
} else {
return emio::err::invalid_data;
}
}
emio::result<void> parse(std::string_view sv) {
EMIO_TRY(int val, parse_one(sv));
emio::result<int> res = parse_one(sv);
if (!res) {
return res.error();
}
if (res.assume_value() == val) {
return emio::success;
}
return emio::err::invalid_format;
}
```
## Buffer
An abstract class which provides functionality for receiving a contiguous memory region of chars to write into.
There exist multiple implementation of a buffer, all fulfilling a different use case.
Some buffers have an internal cache to provide a contiguous memory if the actually output object doesn't provide on.
Additionally, some buffers can be reset to reuse the total capacity of the storage for the next operation.
This invalidates any obtaining view!
### memory_buffer
- An endless growing buffer with an internal storage for small buffer optimization.
*Example*
```cpp
emio::memory_buffer buf;
emio::result<std::span<char>> area = buf.get_write_area_of(50);
assert(area);
std::string_view view = buf.view();
assert(buf.capacity() >= view.size());
std::string str = buf.str();
buf.reset();
```
### span_buffer
- A buffer over a specific contiguous range.
*Example*
```cpp
std::array<char, 512> storage;
emio::span_buffer buf{storage};
assert(buf.capacity() == 512);
emio::result<std::span<char>> area = buf.get_write_area_of(50);
assert(area);
std::string_view view = buf.view();
std::string str = buf.str();
buf.reset();
```
### static_buffer
- A buffer containing a fixed-size storage.
*Example*
```cpp
emio::static_buffer<512> buf{storage};
assert(buf.capacity() == 512);
emio::result<std::span<char>> area = buf.get_write_area_of(50);
assert(area);
std::string_view view = buf.view();
std::string str = buf.str();
buf.reset();
```
### iterator_buffer
- A buffer for all kinds of output iterators (raw-pointers, back_insert_iterator or any other output iterator).
- The buffer's with a direct output iterator (e.g. std::string::iterator) do have an internal cache.
*Example*
```cpp
std::string storage{"filled with something up"};
emio::iterator_buffer buf{std::back_inserter(storage)};
emio::result<std::span<char>> area = buf.get_write_area_of(50);
assert(area);
assert(buf.flush());
std::back_insert_iterator<std::string> out = buf.out();
```
### file_buffer
- A buffer over an std::File (file stream) with an internal cache.
*Example*
```cpp
std::FILE* file = std::fopen("test", "w");
emio::file_buffer buf{file};
emio::result<std::span<char>> area = buf.get_write_area_of(50);
assert(area);
assert(buf.flush());
buf.reset();
```
### truncating_buffer
- A buffer which truncates the remaining output if the limit of another provided buffer is reached.
*Example*
```cpp
emio::static_buffer<48> primary_buf{};
emio::truncating_buffer buf{primary_buf, 32};
emio::result<std::span<char>> area = buf.get_write_area_of(50);
assert(area);
assert(buf.flush());
assert(primary_buf.view().size() == 32); // Only 32 bytes are flushed.
```
## Reader
` class reader;`
- A class to read and parse a char sequence like a finite input stream.
*constructor(input)*
- Constructable from any suitable char sequence.
*Example*
```cpp
emio::reader input{"1"};
assert(input.cnt_remaining() == 1);
assert(input.view_remaining() == "1");
std::string foo{"foo"};
emio::reader input2{foo};
assert(input2.cnt_remaining() == 3);
assert(input2.view_remaining() == "foo");
```
`peek() -> result<char>`
- Returns the next char without consuming it.
*Example*
```cpp
emio::reader input{"abc"};
emio::result<char> res = input.peek();
assert(res == 'a');
```
`read_char() -> result<char>`
- Returns one char.
*Example*
```cpp
emio::reader input{"abc"};
emio::result<char> res = input.read_char();
assert(res == 'a');
```
`read_n_char(n) -> result<string_view>`
- Returns *n* chars.
*Example*
```cpp
emio::reader input{"abc"};
emio::result<std::string_view> res = input.read_n_char(3);
assert(res == "abc");
```
`parse_int<T>(base = 10) -> result<T>`
- Parses an integer of type *T* with a specific *base*.
*Example*
```cpp
emio::reader input{"abc"};
emio::result<int> res = input.read_int(16 /* hexadecimal */);
assert(res == 0xabc);
```
`read_until/_char/str/any_of/none_of/([predicate,] options) -> result<string_view>`
- Reads n chars until a given *predicate* (delimiter/group/function) applies.
- Has *options* to configure what should happen with the predicate and what should happen if EOF is reached.
*Example*
```cpp
emio::reader get_input() {
return emio::reader{"abc"};
}
// read_until_char
emio::result<std::string_view> res = get_input().read_until_char('c');
assert(res == "ab");
// read_until_str
emio::result<std::string_view> res = get_input().read_until_str("bc");
assert(res == "a");
// read_until_any_of
emio::result<std::string_view> res = get_input().read_until_any_of("cd");
assert(res == "ab");
// read_until_none_of
emio::result<std::string_view> res = get_input().read_until_none_of("ab");
assert(res == "ab");
// read_until with predicate
emio::result<std::string_view> res = get_input().read_until([](char c) { return c != 'a';});
assert(res == "a");
```
```cpp
// Different options.
emio::reader input{"abc 123 Hello"};
// with include_delimiter option
emio::result<std::string_view> res = input.read_until_str(" ", {.include_delimiter = true});
assert(res == "abc ");
// with keep keep_delimiter option
emio::result<std::string_view> res = input.read_until_str("Hello", {.keep_delimiter = true});
assert(res == "123 ");
// with ignore_eof option
emio::result<std::string_view> res = input.read_until_str("xyz", {.ignore_eof = true});
assert(res == "Hello");
```
`read_if_match_char/str(c/str) -> result<char/std::string_view>`
- Reads one/multiple chars if *c/str* matches the next char/chars.
*Example*
```cpp
emio::reader input{"abc"};
if (input.read_if_match_char('a')) {
emio::result<std::string_view> res = input.read_if_match_str("bc");
assert(res == "bc");
}
```
## Writer
`class writer;`
- A class to write sequences of characters or other kinds of data into an output buffer.
*constructor(buffer)*
*Example*
```cpp
emio::static_buffer<128> buf;
emio::writer output{buf};
```
- Constructable from a reference to a buffer.
`write_char(c) -> result<void>`
- Writes a char *c* into the buffer.
*Example*
```cpp
emio::writer output{get_buffer()};
emio::result<void> res = output.write_char('a'); // Buffer contains "a"
assert(res);
```
`write_char_n(c, n) -> result<void>`
- Writes a char *c* *n* times into the buffer.
*Example*
```cpp
emio::writer output{get_buffer()};
emio::result<void> res = output.write_char_n('a', 5); // Buffer contains "aaaaa"
assert(res);
```
`write_char_escaped(c) -> result<void>`
- Writes a char *c* escaped into the buffer.
*Example*
```cpp
emio::writer output{get_buffer()};
emio::result<void> res = output.write_char_escaped('\n', 5); // Buffer contains "\\n"
assert(res);
```
`write_str(sv) -> result<void>`
- Writes a char sequence *sv* into the buffer.
*Example*
```cpp
emio::writer output{get_buffer()};
emio::result<void> res = output.write_str("Hello"); // Buffer contains "Hello"
assert(res);
```
`write_str_escaped(sv) -> result<void>`
- Writes a char sequence *sv* escaped into the buffer.
*Example*
```cpp
emio::writer output{get_buffer()};
emio::result<void> res = output.write_str("\t 'and'"); // Buffer contains "\\t \'and\'"
assert(res);
```
`write_int(integer, options) -> result<void>`
- Writes an *integer* into the buffer.
- Has *options* to configure the base and if the alphanumerics should be in lower or upper case.
*Example*
```cpp
emio::writer output{get_buffer()};
emio::result<void> res = output.write_int(15); // Buffer contains "15"
assert(res);
res = output.write_int(15, {.base = 16, upper_case = true}); // Buffer contains "15F"
assert(res);
```
## Format
The following functions use a format string syntax which is nearly identical to the one used in
[fmt](https://fmt.dev/latest/syntax.html), which is similar to
[str.format](https://docs.python.org/3/library/stdtypes.html#str.format) in Python.
Things that are missing:
- chrono syntax (planned)
- 'a'/'A' for hexadecimal floating point format (TBD)
- UTF-8 support (TBD)
- using an identifier as arg_id: `fmt::format("{nbr}", fmt::arg("nbr", 42)` (TBD)
- `'L'` options for locale (somehow possible but not with std::locale because of the binary size)
The grammar for the replacement field is as follows:
```sass
replacement_field ::= "{" [arg_id] [":" format_spec] "}"
arg_id ::= integer
integer ::= digit+
digit ::= "0"..."9"
```
The grammar for the format specification is as follows:
```sass
format_spec ::= [[fill]align][sign]["#"]["0"][width][type]
fill ::= <a character other than '{' or '}'>
align ::= "<" | ">" | "^"
sign ::= "+" | "-" | " "
width ::= integer
type ::= "b" | "B" | "c" | "d" | "o" | "s" | "x" | "X" | "e" | "E" | "f" | "F" | "g" | "G"
```
The syntax of the format string is validated at compile-time. If a validation at runtime is required, the string
must be wrapped inside a `runtime_string` object. There is a simple helper function for that:
`runtime(string_view) -> runtime_string`
Some functions (like `format` or `formatted_size`) are further optimized (simplified) in their return type if the format
string is a valid-only format string that could be ensured at compile-time.
`format(format_str, ...args) -> string/result<string>`
*Example*
```cpp
std::string str = emio::format("Hello {}!", 42);
assert(str == "Hello 42!");
std::string format_str = "Good by {}!";
emio::result<std::string> res = emio::format(emio::runtime(format_str), 42);
assert(res == "Good by 42!");
```
- Formats arguments according to the format string, and returns the result as a string.
- The return value depends on the type of the format string (valid-only type or not).
`format_to(out, format_str, ...args) -> result<Output>`
- Formats arguments according to the format string, and writes the result to the output iterator/buffer.
**Note** If a raw output pointer or simple output iterator is used, no range checking can take place!
*Example*
```cpp
std::string out;
out.resize(10);
emio::result<std::string::iterator> res = emio::format_to(out.begin(), "Hello {}!", 42);
assert(res);
assert(out == "Hello 42!");
```
`format_to_n(out, n, format_str, ...args) -> result<format_to_n_result<Output>>`
- Formats arguments according to the format string, and writes the result to the output iterator/buffer. At most *n*
characters are written.
*Example*
```cpp
std::string out;
out.resize(10);
emio::result<emio::format_to_n_result<std::string::iterator>> res =
emio::format_to_n(out.begin(), 7, "Hello {}!", 42);
assert(res)
assert(res->out == "Hello 4");
assert(res->size == 7);
```
`formatted_size(format_str, ...args) -> size_t/result<size_t>`
- Determines the total number of characters in the formatted string by formatting args according to the format string.
- The return value depends on the type of the format string (valid-only type or not).
*Example*
```cpp
size_t size = emio::formatted_size("> {}", 42);
assert(size == 4);
```
For each function there exists a function prefixed with v (e.g. `vformat`) which takes `format_args` instead of a
format string and arguments. The types are erased and can be used in non-template functions to reduce build-time, hide
implementations and reduce the binary size. **Note:** These type erased functions cannot be used at compile-time.
`format_args` can be created with:
`make_format_args(format_str, ...args) -> internal format_args_storage`
- Returns an object that stores a format string with an array of all arguments to format.
- Keep in mind that the storage uses reference semantics and does not extend the lifetime of args. It is the
programmer's responsibility to ensure that args outlive the return value.
*Example*
```cpp
emio::result<void> internal_info(const emio::format_args& args) {
emio::memory_buffer buf;
emio::writer out{buf}; // Prefix message.
EMIO_TRYV(out.write_str("INFO: "));
EMIO_TRYV(emio::vformat_to(out, args));
log_message(out.view()); // Forward result.
return emio::success;
}
template<typename...Args>
void log_info(emio::format_string<Args...> fmt, const Args&...args) {
emio::result<void> res = internal_info(emio::make_format_args(fmt, args...)); // type-erasing takes place
res.value(); // Throw on any error.
}
void do_something(int i) {
log_info("Do something started with {}.", i);
}
int main() {
do_something(42); // INFO: Do something started with 42.
}
```
### Dynamic format specification
Unlike other libraries, the format specification cannot be changed through extra replacement fields, as it is possible
e.g. with fmt to dynamically set the precision to 1 with `fmt::format("{:.{}f}", 3.14, 1);`.
With emio it is possible to dynamically define _width_ and _precision_ through a `format_spec` object which is then
passed as an argument with the original value to the format function.
`format_spec{.width = <width>, .precision = <precision>}`
- If a spec is not defined inside the struct, the spec of the parsed format string will be applied.
*Example*
```cpp
emio::format_spec spec{.precision = 1};
emio::format("{}", spec.with(3.141592653)); // 3.1
```
### Formatter
There exists formatter for builtin types like bool, char, string, integers, floats, void* and non-scoped enums, ranges
and tuple like types. Support for other standard types (e.g. chrono duration, optional) is planned.
For formatting values of pointer-like types, simply use `emio::ptr(p)`.
*Example*
```cpp
int* value = get();
emio::format("{}", emio::ptr(value));
```
Use `is_formattable_v<Type>` to check if a type is formattable.
A formatter exists of one optional function `validate` and two mandatory functions `parse` and `format`. If `validate`
is not present, `parse` must validate the format string.
*Example*
```cpp
struct foo {
int x;
};
template <>
class emio::formatter<foo> {
public:
/**
* Optional static function to validate the format string syntax for this type.
* @note If not present, the parse function is invoked for validation.
* @param format_rdr The reader over the format string.
* @return Success if the format string is valid.
*/
static constexpr result<void> validate(reader& format_rdr) noexcept {
return format_rdr.read_if_match_char('}');
}
/**
* Function to parse the format specs for this type.
* @param format_rdr The reader over the format string.
* @return Success if the format string is valid and could be parsed.
*/
constexpr result<void> parse(reader& format_rdr) noexcept {
return format_rdr.read_if_match_char('}');
}
/**
* Function to format the object of this type according to the parsed format specs.
* @param out The output writer.
* @param arg The argument to format.
* @return Success if the formatting could be done.
*/
constexpr result<void> format(writer& out, const foo& arg) const noexcept {
return wtr.write_int(arg.x);
}
};
int main() {
emio::format("{}", foo{42}); // 42
}
```
It is also possible to reuse existing formatters via inheritance or composition.
*Example*
```cpp
struct foo {
int x;
};
template <>
class emio::formatter<foo> : public emio::format<int> {
public:
constexpr result<void> format(writer& out, const foo& arg) noexcept {
return emio::format<int>::format(wtr, arg.x);
}
};
int main() {
emio::format("{:#x}", foo{42}); // 0x2a
}
```
If the `validate` (or if absent the `parse`) function is not constexpr, a runtime format strings must be used. The
`format` function don't need to be constexpr if the formatting shouldn't be done at compile-time.
For simple type formatting, like formatting an enum class to its underlying integer or to a string, the function
`format_as` could be provided. The function must be in the same namespace since ADL is used.
*Example*
```cpp
namespace foo {
enum class bar {
foobar,
barfoo
};
constexpr auto format_as(const bar& w) noexcept {
return static_cast<std::underlying_type_t<bar>>(w);
}
}
```
## Print
It is possible to directly print to the standard output or other file streams.
`print(format_str, ...args) -> void/result<void>`
- Formats arguments according to the format string, and writes the result to the standard output stream.
- The return value depends on the type of the format string (valid-only type or not).
*Example*
```cpp
emio::print("{}!", 42); // Outputs: "42!"
emio::result<void> res = emio::print(emio::runtime("{}!"), 42); // Outputs: "42!"
assert(res);
```
`print(file, format_str, ...args) -> result<void>`
- Formats arguments according to the format string, and writes the result to a file stream.
*Example*
```cpp
emio::result<void> res = emio::print(stderr, "{}!", 42); // Outputs: "42!" to stderr
assert(res);
```
`println(format_str, ...args) -> void/result<void>`
- Formats arguments according to the format string, and writes the result to the standard output stream with a new line
at the end.
- The return value depends on the type of the format string (valid-only type or not).
*Example*
```cpp
emio::println("{}!", 42); // Outputs: "42!" with a line break
emio::result<void> res = emio::println(emio::runtime("{}!"), 42); // Outputs: "42!" with a line break
assert(res);
```
`println(file, format_str, ...args) -> result<void>`
- Formats arguments according to the format string, and writes the result to a file stream with a new line at the end.
*Example*
```cpp
emio::result<void> res = emio::println(stderr, "{}!", 42); // Outputs: "42!" with a line break to stderr
assert(res);
```
For each function there exists a function prefixed with v (e.g. `vprint`) which allow the same functionality as
e.g. `vformat(...)` does for `format(...)`.
## Scan
The following functions use a format string syntax which is similar to the format syntax of `format`.
The grammar for the replacement field is the same. The grammar for the scan specific syntax is as follows:
```sass
format_spec ::= ["#"][width][type]
type ::= "b" | "B" | "c" | "d" | "o" | "s" | "x" | "X"
```
`#`
- for integral types: the alternate form
- b/B: `0b` (e.g. 0b10110)
- d: nothing (e.g. 9825)
- o: leading `0` (e.g. 057)
- x/X: `0x` (e.g 0x2fA3)
- if `#` is present but not the `type`, the base is deduced from the scanned alternate form.
*Example*
```cpp
int i;
int j;
int k;
int l;
scan("0b101 101 0101 0x101", "{:#} {:#} {:#} {:#}", i, j, k, l);
assert(i == 0b101);
assert(j == 101);
assert(k == 0101);
assert(l == 0x101);
```
`width`
- specifies the number of characters to include when parsing an argument
*Example*
```cpp
int i;
std::string_view j;
int k;
scan("125673", "{:2}{:3}{}", i, j, k);
assert(i == 12);
assert(j == "567");
assert(k == 3);
```
`type`
- for integral types: the base to assume
- b/B: base 2 (binary)
- d: base 10 (decimal)
- o: base 8 (octal)
- x/X: base 16 (hexadecimal)
- c for char
- s for string/string_view
*Example*
```cpp
int i;
int j;
int k;
int l;
scan("101 101 101 101", "{:b} {:d} {:o} {:x}", i, j, k, l);
assert(i == 0b101);
assert(j == 101);
assert(k == 0101);
assert(l == 0x101);
```
The syntax of the format string is validated at compile-time. If a validation at runtime is required, the string must
be wrapped inside a `runtime_string` object. There is a simple helper function for that:
`runtime(string_view) -> runtime_string`
The API is structured as follows:
`scan(input, format_str, ...args) -> result<void>`
*Example*
```cpp
int32_t i;
uint32_t j;
emio::result<void> res = emio::scan("-1,2", "{},{}", i, j);
assert(res);
assert(i == -1);
assert(j == 2);
```
- Scans the input string for the given arguments according to the format string.
`scan_from(reader, format_str, ...args) -> result<void>`
- Scans the content of the reader for the given arguments according to the format string.
*Example*
```cpp
int32_t i;
uint32_t j;
emio::reader input{"-1,2..."};
emio::result<void> res = emio::scan_from(input, "{},{}", i, j);
assert(res);
assert(i == -1);
assert(j == 2);
assert(input.view_remaining() == "...");
```
For each function there exists a function prefixed with v (e.g. `vscan`) which takes `scan_args` instead of a format
string and arguments. The types are erased and can be used in non-template functions to reduce build-time, hide
implementations and reduce the binary size. **Note:** These type erased functions cannot be used at compile-time.
`scan_args` can be created with:
`make_scan_args(format_str, ...args) -> internal scan_args_storage`
- Returns an object that stores a format string with an array of all arguments to scan.
- Keep in mind that the storage uses reference semantics and does not extend the lifetime of args. It is the
programmer's responsibility to ensure that args outlive the return value.
### Scanner
There exists scanner for builtin types like char, string and integers. Support for other types (e.g. float) is planned.
Use `is_scanner_v<Type>` to check if a type is scannable.
A scanner exists of one optional function `validate` and two mandatory functions `parse` and `scan`. If `validate`
is not present, `parse` must validate the format string.
*Example*
```cpp
struct foo {
int x;
};
template <>
class emio::scanner<foo> {
public:
/**
* Optional static function to validate the format string syntax for this type.
* @note If not present, the parse function is invoked for validation.
* @param format_rdr The reader over the format string.
* @return Success if the format string is valid.
*/
static constexpr result<void> validate(reader& format_rdr) noexcept {
return format_rdr.read_if_match_char('}');
}
/**
* Function to parse the format specs for this type.
* @param format_rdr The reader over the format string.
* @return Success if the format string is valid and could be parsed.
*/
constexpr result<void> parse(reader& format_rdr) noexcept {
return format_rdr.read_if_match_char('}');
}
/**
* Function to scan the object of this type according to the parsed format specs.
* @param input The input reader.
* @param arg The argument to scan.
* @return Success if the scanning could be done.
*/
constexpr result<void> scan(reader& input, foo& arg) const noexcept {
EMIO_TRYV(input.read_int(arg.x));
return success;
}
};
int main() {
foo f{};
emio::scan("42", "{}", i); // f.x == 42
}
```
It is also possible to reuse existing scanner via inheritance or composition.
*Example*
```cpp
struct foo {
int x;
};
template <>
class emio::scanner<foo> : public emio::scanner<int> {
public:
constexpr result<void> scan(reader& input, foo& arg) const noexcept {
return emio::scanner<int>::scan(input, arg.x);
}
};
int main() {
foo f{};
emio::scan("0x2A", "{:x}", f); // f.x == 42
}
```
If the `validate` (or if absent the `parse`) function is not constexpr, a runtime strings must be used. The `scan`
function don't need to be constexpr if the scanning shouldn't be done at compile-time.
================================================
FILE: docs/CODE_OF_CONDUCT.md
================================================
# Code of Conduct
* You will be judged by your contributions first, and your sense of humor second.
* Nobody owes you anything.
================================================
FILE: docs/CONTRIBUTING.md
================================================
# Contributing
<!--
Short overview, rules, general guidelines, notes about pull requests and
style should go here.
-->
## Code of Conduct
Please see the [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) document.
## Getting started
Helpful notes for developers can be found in the [`DEVELOPING.md`](DEVELOPING.md) document.
================================================
FILE: docs/DESIGN.md
================================================
# Design
This is a short design overview of *emio*.
**Note:** [fmtlib/fmt](https://github.com/fmtlib/fmt) is an awesome library with great concepts and implementations
which are partly reused in this library. All statements made here by comparing *emio* with *fmt* are only objective and
shouldn't insult anybody's work.
## Binary footprint
The API provides a low level API for reading or writing builtin-types and also a high-level API for parsing (TODO) or
formatting, which reuses the low-level API. Additional, even when using the high-level API, only used types increase the
binary size. Furthermore, since the parse or format string can be validated at compile-time, the validation can be
omitted at runtime.
This is e.g. different in *fmt*, which doesn't provide a low-level API. Also, *fmt* instantiates all builtin types (e.g.
bool, int64_t, int128_t, long double...) even if only int32_t should be formatted. Last but not least, the format
string is validated at compile-time and runtime.
Take the following code snippet as example which is compiled and statically linked.
```cpp
std::string f = emio::format("{}", 1);
```
If compiled with *fmt*, **191 kBytes** of flash memory is required. If *emio* is used, only **5 kBytes** are requires.
This is **38 times** less! Keep in mind that flash memory of many microcontrollers is between 128 kBytes and 2 MBytes.
This huge advantage of *emio* comes with a price: *emio* doesn't support all features of *fmt*. But these features are
likely not so important for embedded systems. Some missing features are:
- no std::locale support (no internationalization)
- if a runtime format string is used, validation and parsing happens sequential (performance overhead)
- some features cannot be API compatible and have to be done differently (e.g. make_format_args requires the format
string or dynamic width and precision is implemented by a wrapper object)
## Result type
Instead of C++-exceptions, `emio:result` is used for returning and propagating errors. It is similar to `std::expected`,
`boost::outcome` or Rust's `Result`.
`emio::result<T>` does either holds the expected value of type T or an unexpected error of type `emio::err`. Through
observer methods, the state and the value or the error can be visited.
```cpp
emio::result<std::string> format(...) noexcept;
emio::result<char> get_first_digit_of_pi() noexcept {
emio::result<std::string> pi_as_str = format("{}", M_PI);
if (pi_as_str) {
return pi_as_str->at(0);
}
return emio::err::invalid_data;
}
```
To reduce the if/else chain, emio provides two simple macros (similar to *boost::outcome*) `EMIO_TRY` and `EMIO_TRYV`.
The above function could be rewritten into:
```cpp
emio::result<std::string> format(...) noexcept;
emio::result<char> get_first_digit_of_pi() noexcept {
EMIO_TRY(std::string pi_as_str, format("{}", M_PI));
return pi_as_str.at(0);
}
```
The biggest advantage of using a result type is that error handling is still possible even if C++-exceptions are
disabled. Unlike other libraries like *fmt*, which mostly terminate deep inside their library if an error occurs, the
result object propagates the error back to the callee. The drawback is of course the small performance overhead and
the explicit handling of the control flow.
## Performance
The current benchmarks show that the formatting is round about 1.5 - 2 times slower than fmtlib and the performance is
similar to printf (e.g. for integer types). Scanning on the other hand is around twice as fast then scanf. See the
benchmark tests inside the CI for more details.
## Class diagram

See also [API](API.md) notes.
================================================
FILE: docs/DEVELOPING.md
================================================
# Contributing
Here is some wisdom to help you build and test this project as a developer and potential contributor.
The project template is generated with [cmake-init](https://github.com/friendlyanon/cmake-init).
If you plan to contribute, please read the [CONTRIBUTING](CONTRIBUTING.md) guide.
## Install
Only two commands are necessary to install this library on your system.
```sh
cmake -S . -B build
cmake --install build
```
## Developer mode
Build system targets that are only useful for developers of this project are hidden if the `emio_DEVELOPER_MODE` option
is disabled. Enabling this option makes tests and other developer targets and options available. Not enabling this
option means that you are a consumer of this project and thus you have no need for these targets and options.
Developer mode is always set to on in CI workflows.
### Presets
This project makes use of [presets][1] to simplify the process of configuring the project. As a developer, you are
recommended to always have the [latest CMake version][2] installed to make use of the latest Quality-of-Life additions.
You have a few options to pass `emio_DEVELOPER_MODE` to the configure command, but this project prefers to use presets.
As a developer, you should create a `CMakeUserPresets.json` file at the root of the project:
```json
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 14,
"patch": 0
},
"configurePresets": [
{
"name": "dev",
"binaryDir": "${sourceDir}/build/dev",
"inherits": ["dev-mode", "ci-<os>"],
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
}
],
"buildPresets": [
{
"name": "dev",
"configurePreset": "dev",
"configuration": "Debug"
}
],
"testPresets": [
{
"name": "dev",
"configurePreset": "dev",
"configuration": "Debug",
"output": {
"outputOnFailure": true
}
}
]
}
```
You should replace `<os>` in your newly created presets file with the name of the operating system you have, which may
be `win64` or `unix`. You can see what these correspond to in the [`CMakePresets.json`](CMakePresets.json) file.
`CMakeUserPresets.json` is also the perfect place in which you can put all sorts of things that you would otherwise want
to pass to the configure command in the terminal.
### Configure, build and test
If you followed the above instructions, then you can configure, build and test the project respectively with the
following commands from the project root on any operating system with any build system:
```sh
cmake --preset=dev
cmake --build --preset=dev
ctest --preset=dev
```
Please note that both the build and test command accepts a `-j` flag to specify the number of jobs to use, which should
ideally be specified to the number of threads your CPU has. You may also want to add that to your preset using the
`jobs` property, see the [presets documentation][1] for more details.
[1]: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
[2]: https://cmake.org/download/
================================================
FILE: docs/res/class_diagram.puml
================================================
@startuml
class "scan functions" << (F,#FF7700) >> {
scan(input, format_str, args...) -> result<void>
}
class "format functions" << (F,#FF7700) >> {
format(format_str, args...) -> result<std::string>
}
class reader
class "std::string_view" as sv
reader *-- sv
abstract class buffer
class memory_buffer
class static_buffer
class span_buffer
class iterator_buffer
buffer <|-- iterator_buffer
buffer <|-- static_buffer
buffer <|-- span_buffer
buffer <|-- memory_buffer
class writer
writer o-- buffer
iterator_buffer o-- "output iterator"
package "output iterator" {
class truncating_iterator
class "std::vector<T>::iterator"
class "const char *"
}
package "contiguous container" {
class "std::array"
class "std::string" as s2
class "std::vector"
}
span_buffer o-- "contiguous container"
class format_string
class formatter<T - Type to format> {
validate(reader&) [0..1]
parse(reader&)
format(writer& out, T)
}
format_string -.> "validate & parse" formatter
"format functions" *-.- "format_string"
"format functions" -.-> "writer" : "write formatted\noutput"
"format functions" -.-> "reader" : read format\nstring
"format functions" -.-> "formatter" : parse,\nformat
class format_scan_string
class scanner<T - Type to scan> {
validate(reader&) [0..1]
parse(reader&)
scan(reader& in, T&)
}
format_scan_string -.> "validate & parse" scanner
"scan functions" *-.- "format_scan_string"
"scan functions" -.-> "reader" : read input & \nformat string
"scan functions" -.-> "scanner" : parse,\nscan
@enduml
================================================
FILE: include/emio/buffer.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include <iterator>
#include <limits>
#include <span>
#include <utility>
#if __STDC_HOSTED__
# include <string>
#endif
#include <string_view>
#include <type_traits>
#include "detail/ct_vector.hpp"
#include "result.hpp"
namespace emio {
/// The default cache size of buffers with an internal cache.
inline constexpr size_t default_cache_size{128};
/**
* This class provides the basic API and functionality for receiving a contiguous memory region of chars to write into.
* @note Use a specific subclass for a concrete instantiation.
*/
class buffer {
public:
constexpr buffer(const buffer& other) = delete;
constexpr buffer(buffer&& other) = delete;
constexpr buffer& operator=(const buffer& other) = delete;
constexpr buffer& operator=(buffer&& other) = delete;
virtual constexpr ~buffer() noexcept = default;
/**
* Returns a write area with the requested size on success.
* @param size The size the write area should have.
* @return The write area with the requested size on success or eof if no write area is available.
*/
constexpr result<std::span<char>> get_write_area_of(size_t size) noexcept {
EMIO_TRY(const std::span<char> area, get_write_area_of_max(size));
if (area.size() < size) {
used_ -= area.size();
return err::eof;
}
return area;
}
/**
* Returns a write area which may be smaller than the requested size.
* @note This function should be used to support subclasses with a limited internal buffer.
* E.g. Writing a long string in chunks.
* @param size The size the write area should maximal have.
* @return The write area with the requested size as maximum on success or eof if no write area is available.
*/
constexpr result<std::span<char>> get_write_area_of_max(size_t size) noexcept {
// If there is enough remaining capacity in the current write area, return it.
// Otherwise, request a new write area from the concrete implementation.
// There is a special case for fixed size buffers. Since they cannot grow, they simply return the
// remaining capacity or EOF, if hitting zero capacity.
const size_t remaining_capacity = area_.size() - used_;
if (remaining_capacity >= size || fixed_size_ == fixed_size::yes) {
if (remaining_capacity == 0 && size != 0) {
return err::eof;
}
const size_t max_size = std::min(remaining_capacity, size);
const std::span<char> area = area_.subspan(used_, max_size);
used_ += max_size;
return area;
}
EMIO_TRY(const std::span<char> area, request_write_area(used_, size));
used_ += area.size();
return area;
}
protected:
/// Flag to indicate if the buffer's size is fixed and cannot grow.
enum class fixed_size : bool { no, yes };
/**
* Constructs the buffer.
* @brief fixed Flag to indicate if the buffer's size is fixed and cannot grow.
*/
constexpr explicit buffer(fixed_size fixed = fixed_size::no) noexcept : fixed_size_{fixed} {}
/**
* Requests a write area of the given size from a subclass.
* @param used Already written characters into the current write area.
* @param size The requested size of a new write area.
* @return The write area with the requested size as maximum on success or eof if no write area is available.
*/
virtual constexpr result<std::span<char>> request_write_area(const size_t used, const size_t size) noexcept {
static_cast<void>(used); // Keep params for documentation.
static_cast<void>(size);
return err::eof;
}
/**
* Sets a new write area in the base class object to use.
* @param area The new write area.
*/
constexpr void set_write_area(const std::span<char> area) noexcept {
area_ = area;
used_ = 0;
}
/**
* Returns the count of written characters of the current hold write area.
* @return The count.
*/
[[nodiscard]] constexpr size_t get_used_count() const noexcept {
return used_;
}
private:
fixed_size fixed_size_{fixed_size::no};
size_t used_{};
std::span<char> area_{};
};
/**
* This class fulfills the buffer API by providing an endless growing buffer.
* @tparam StorageSize The size of the internal storage used for small buffer optimization.
*/
template <size_t StorageSize = default_cache_size>
class memory_buffer final : public buffer {
public:
/**
* Constructs and initializes the buffer with the internal storage size.
*/
constexpr memory_buffer() noexcept : memory_buffer{0} {}
/**
* Constructs and initializes the buffer with the given capacity.
* @param capacity The initial capacity.
*/
constexpr explicit memory_buffer(const size_t capacity) noexcept {
// Request at least the internal storage size. Should never fail.
request_write_area(0, std::max(vec_.capacity(), capacity)).value();
}
constexpr memory_buffer(const memory_buffer& other)
: buffer{}, used_{other.used_ + other.get_used_count()}, vec_{other.vec_} {
this->set_write_area({vec_.data() + used_, vec_.data() + vec_.capacity()});
}
constexpr memory_buffer(memory_buffer&& other) noexcept
: buffer{}, used_{other.used_ + other.get_used_count()}, vec_{std::move(other).vec_} {
this->set_write_area({vec_.data() + used_, vec_.data() + vec_.capacity()});
other.reset();
}
constexpr memory_buffer& operator=(const memory_buffer& other) {
if (&other == this) {
return *this;
}
used_ = other.used_ + other.get_used_count();
vec_ = other.vec_;
this->set_write_area({vec_.data() + used_, vec_.data() + vec_.capacity()});
return *this;
}
constexpr memory_buffer& operator=(memory_buffer&& other) noexcept {
if (&other == this) {
return *this;
}
used_ = other.used_ + other.get_used_count();
vec_ = std::move(other).vec_;
this->set_write_area({vec_.data() + used_, vec_.data() + vec_.capacity()});
other.reset();
return *this;
}
constexpr ~memory_buffer() override = default;
/**
* Obtains a view over the underlying string object.
* @return The view.
*/
[[nodiscard]] constexpr std::string_view view() const noexcept {
return {vec_.data(), used_ + this->get_used_count()};
}
#if __STDC_HOSTED__
/**
* Obtains a copy of the underlying string object.
* @return The string.
*/
[[nodiscard]] std::string str() const {
return std::string{view()};
}
#endif
/**
* Resets the buffer's read and write position to the beginning of the internal storage.
*/
constexpr void reset() noexcept {
used_ = 0;
vec_.clear();
request_write_area(0, vec_.capacity()).value();
}
/**
* Returns the number of chars that the buffer has currently allocated space for.
* @return The capacity.
*/
[[nodiscard]] constexpr size_t capacity() const noexcept {
return vec_.capacity();
}
protected:
constexpr result<std::span<char>> request_write_area(const size_t used, const size_t size) noexcept override {
const size_t new_size = vec_.size() + size;
vec_.reserve(new_size);
used_ += used;
const std::span<char> area{vec_.data() + used_, size};
this->set_write_area(area);
return area;
}
private:
size_t used_{};
detail::ct_vector<char, StorageSize> vec_{};
};
/**
* This class fulfills the buffer API by using a span over an contiguous range.
*/
class span_buffer : public buffer {
public:
/**
* Constructs and initializes the buffer with an empty span.
*/
constexpr span_buffer() : buffer{fixed_size::yes} {};
/**
* Constructs and initializes the buffer with the given span.
* @param span The span.
*/
constexpr explicit span_buffer(const std::span<char> span) noexcept : buffer{fixed_size::yes}, span_{span} {
this->set_write_area(span_);
}
constexpr span_buffer(const span_buffer& other) : buffer{fixed_size::yes}, span_{other.span_} {
this->set_write_area(span_);
get_write_area_of(other.get_used_count()).value();
}
// NOLINTNEXTLINE(performance-move-constructor-init): optimized move not possible
constexpr span_buffer(span_buffer&& other) noexcept : span_buffer{std::as_const(other)} {}
constexpr span_buffer& operator=(const span_buffer& other) {
if (&other == this) {
return *this;
}
span_ = other.span_;
this->set_write_area(span_);
get_write_area_of(other.get_used_count()).value();
return *this;
}
constexpr span_buffer& operator=(span_buffer&& other) noexcept {
*this = std::as_const(other);
return *this;
}
constexpr ~span_buffer() override;
/**
* Obtains a view over the underlying string object.
* @return The view.
*/
[[nodiscard]] constexpr std::string_view view() const noexcept {
return {span_.data(), this->get_used_count()};
}
#if __STDC_HOSTED__
/**
* Obtains a copy of the underlying string object.
* @return The string.
*/
[[nodiscard]] std::string str() const {
return std::string{view()};
}
#endif
/**
* Resets the buffer's read and write position to the beginning of the span.
*/
constexpr void reset() noexcept {
this->set_write_area(span_);
}
/**
* Returns the number of chars that the buffer has space for.
* @return The capacity.
*/
[[nodiscard]] constexpr size_t capacity() const noexcept {
return span_.size();
}
private:
std::span<char> span_;
};
// Out-of-line definition because of a GCC bug (93413). Fixed in GCC 13.
inline constexpr span_buffer::~span_buffer() = default;
/**
* This class fulfills the buffer API by providing a fixed-size storage.
* @tparam StorageSize The size of the storage.
*/
template <size_t StorageSize>
class static_buffer final : private std::array<char, StorageSize>, public span_buffer {
public:
/**
* Constructs and initializes the buffer with the storage.
*/
constexpr static_buffer() noexcept : span_buffer{std::span{*this}} {}
constexpr static_buffer(const static_buffer& other) : static_buffer() {
const std::span<char> area = get_write_area_of(other.get_used_count()).value();
detail::copy_n(&*other.begin(), area.size(), area.data());
}
// NOLINTNEXTLINE(performance-move-constructor-init): optimized move not possible
constexpr static_buffer(static_buffer&& other) noexcept : static_buffer(std::as_const(other)) {}
constexpr static_buffer& operator=(const static_buffer& other) {
if (&other == this) {
return *this;
}
set_write_area(std::span{*this});
const std::span<char> area = get_write_area_of(other.get_used_count()).value();
detail::copy_n(&*other.begin(), area.size(), area.data());
return *this;
}
constexpr static_buffer& operator=(static_buffer&& other) noexcept {
*this = std::as_const(other);
return *this;
}
constexpr ~static_buffer() override = default;
// Note: We inherit from std::array to put the storage lifetime before span_buffer.
// Clang will otherwise complain if the storage is a member variable and used during compile-time.
};
namespace detail {
// Extracts a reference to the container from back_insert_iterator.
template <typename Container>
Container& get_container(std::back_insert_iterator<Container> it) noexcept {
using bi_iterator = std::back_insert_iterator<Container>;
struct accessor : bi_iterator {
accessor(bi_iterator iter) : bi_iterator(iter) {}
using bi_iterator::container;
};
return *accessor{it}.container;
}
// Helper struct to get the value type of different iterators.
template <typename T>
struct get_value_type {
using type = typename std::iterator_traits<T>::value_type;
};
template <typename Container>
struct get_value_type<std::back_insert_iterator<Container>> {
using type = typename Container::value_type;
};
#if __STDC_HOSTED__
template <typename Char, typename Traits>
struct get_value_type<std::ostreambuf_iterator<Char, Traits>> {
using type = Char;
};
#endif
template <typename T>
using get_value_type_t = typename get_value_type<T>::type;
template <typename InputIt, typename OutputIt>
constexpr auto copy_str(InputIt it, InputIt end, OutputIt out) -> OutputIt {
while (it != end) {
*out++ = static_cast<char>(*it++);
}
return out;
}
} // namespace detail
/**
* This class template is used to create a buffer around different iterator types.
*/
template <typename Iterator, size_t CacheSize = default_cache_size>
class iterator_buffer;
/**
* This class fulfills the buffer API by using an output iterator and an internal cache.
* @tparam Iterator The output iterator type.
* @tparam CacheSize The size of the internal cache.
*/
template <typename Iterator, size_t CacheSize>
requires(std::input_or_output_iterator<Iterator> &&
std::output_iterator<Iterator, detail::get_value_type_t<Iterator>>)
class iterator_buffer<Iterator, CacheSize> final : public buffer {
public:
/**
* Constructs and initializes the buffer with the given output iterator.
* @param it The output iterator.
*/
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized
constexpr explicit iterator_buffer(Iterator it) noexcept : it_{it} {
this->set_write_area(cache_);
}
iterator_buffer(const iterator_buffer&) = delete;
iterator_buffer(iterator_buffer&&) = delete;
iterator_buffer& operator=(const iterator_buffer&) = delete;
iterator_buffer& operator=(iterator_buffer&&) = delete;
~iterator_buffer() override = default;
/**
* Flushes the internal cache to the output iterator.
* @return Always succeeds.
*/
constexpr result<void> flush() noexcept {
it_ = detail::copy_str(cache_.data(), cache_.data() + this->get_used_count(), it_);
this->set_write_area(cache_);
return success;
}
/**
* Flushes and returns the output iterator at the next write position.
* @return The output iterator.
*/
constexpr Iterator out() noexcept {
flush().assume_value(); // Will never fail.
return it_;
}
protected:
constexpr result<std::span<char>> request_write_area(const size_t /*used*/, const size_t size) noexcept override {
flush().assume_value(); // Will never fail.
const std::span<char> area{cache_};
this->set_write_area(area);
if (size > cache_.size()) {
return area;
}
return area.subspan(0, size);
}
private:
Iterator it_;
std::array<char, CacheSize> cache_;
};
/**
* This class fulfills the buffer API by using an output pointer.
* @tparam Iterator The output iterator type.
*/
template <typename OutputPtr>
requires(std::input_or_output_iterator<OutputPtr*> &&
std::output_iterator<OutputPtr*, detail::get_value_type_t<OutputPtr*>>)
class iterator_buffer<OutputPtr*> final : public buffer {
public:
/**
* Constructs and initializes the buffer with the given output pointer.
* @param ptr The output pointer.
*/
constexpr explicit iterator_buffer(OutputPtr* ptr) noexcept : ptr_{ptr} {
this->set_write_area({ptr, std::numeric_limits<size_t>::max()});
}
iterator_buffer(const iterator_buffer&) = delete;
iterator_buffer(iterator_buffer&&) = delete;
iterator_buffer& operator=(const iterator_buffer&) = delete;
iterator_buffer& operator=(iterator_buffer&&) = delete;
~iterator_buffer() override = default;
/**
* Does nothing. Kept for uniformity with other iterator_buffer implementations.
* @return Always succeeds.
*/
constexpr result<void> flush() noexcept {
// Nothing.
return success;
}
/**
* Returns the output pointer at the next write position.
* @return The output pointer.
*/
constexpr OutputPtr* out() noexcept {
return ptr_ + this->get_used_count();
}
private:
OutputPtr* ptr_;
};
/**
* This class fulfills the buffer API by using the container of an contiguous back-insert iterator.
* @tparam Container The container type of the back-insert iterator.
* @tparam Capacity The minimum initial requested capacity of the container.
*/
template <typename Container, size_t Capacity>
requires std::contiguous_iterator<typename Container::iterator>
class iterator_buffer<std::back_insert_iterator<Container>, Capacity> final : public buffer {
public:
/**
* Constructs and initializes the buffer with the given back-insert iterator.
* @param it The back-insert iterator.
*/
constexpr explicit iterator_buffer(std::back_insert_iterator<Container> it) noexcept
: container_{detail::get_container(it)} {
request_write_area(0, std::min(container_.capacity(), Capacity)).value();
}
iterator_buffer(const iterator_buffer&) = delete;
iterator_buffer(iterator_buffer&&) = delete;
iterator_buffer& operator=(const iterator_buffer&) = delete;
iterator_buffer& operator=(iterator_buffer&&) = delete;
~iterator_buffer() override = default;
/**
* Flushes the back-insert iterator by adjusting the size.
* @return Always succeeds.
*/
constexpr result<void> flush() noexcept {
container_.resize(used_ + this->get_used_count());
return success;
}
/**
* Flushes and returns the back-insert iterator.
* @return The back-insert iterator.
*/
constexpr std::back_insert_iterator<Container> out() noexcept {
flush().assume_value(); // Will never fail.
return std::back_inserter(container_);
}
protected:
constexpr result<std::span<char>> request_write_area(const size_t used, const size_t size) noexcept override {
const size_t new_size = container_.size() + size;
container_.resize(new_size);
used_ += used;
const std::span<char> area{container_.data() + used_, new_size};
this->set_write_area(area);
return area.subspan(0, size);
}
private:
size_t used_{};
Container& container_;
};
template <typename Iterator>
iterator_buffer(Iterator&&) -> iterator_buffer<std::decay_t<Iterator>>;
/**
* This class fulfills the buffer API by using a file stream and an internal cache.
* @tparam CacheSize The size of the internal cache.
*/
template <size_t CacheSize = default_cache_size>
class file_buffer final : public buffer {
public:
/**
* Constructs and initializes the buffer with the given file stream.
* @param file The file stream.
*/
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized
constexpr explicit file_buffer(std::FILE* file) noexcept : file_{file} {
this->set_write_area(cache_);
}
file_buffer(const file_buffer&) = delete;
file_buffer(file_buffer&&) = delete;
file_buffer& operator=(const file_buffer&) = delete;
file_buffer& operator=(file_buffer&&) = delete;
~file_buffer() override = default;
/**
* Flushes the internal cache to the file stream.
* @note Does not flush the file stream itself!
*/
result<void> flush() noexcept {
const size_t written = std::fwrite(cache_.data(), sizeof(char), this->get_used_count(), file_);
if (written != this->get_used_count()) {
return err::eof;
}
this->set_write_area(cache_);
return success;
}
/**
* Resets the buffer's read and write position to the beginning of the file stream.
*/
constexpr void reset() noexcept {
this->set_write_area(cache_);
std::fseek(file_, 0, SEEK_SET);
}
protected:
result<std::span<char>> request_write_area(const size_t /*used*/, const size_t size) noexcept override {
EMIO_TRYV(flush());
const std::span<char> area{cache_};
this->set_write_area(area);
if (size > cache_.size()) {
return area;
}
return area.subspan(0, size);
}
private:
std::FILE* file_;
std::array<char, CacheSize> cache_;
};
/**
* This class fulfills the buffer API by using a primary buffer and an internal cache.
* Only a limited amount of characters is written to the primary buffer. The remaining characters are truncated.
* @tparam CacheSize The size of the internal cache.
*/
template <size_t CacheSize = default_cache_size>
class truncating_buffer final : public buffer {
public:
/**
* Constructs and initializes the buffer with the given primary buffer and limit.
* @param primary The primary buffer.
* @param limit The limit.
*/
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized.
constexpr explicit truncating_buffer(buffer& primary, size_t limit) : primary_{primary}, limit_{limit} {
this->set_write_area(cache_);
}
truncating_buffer(const truncating_buffer&) = delete;
truncating_buffer(truncating_buffer&&) = delete;
truncating_buffer& operator=(const truncating_buffer&) = delete;
truncating_buffer& operator=(truncating_buffer&&) = delete;
constexpr ~truncating_buffer() noexcept override = default;
/**
* Returns the count of the total (not truncated) written characters.
* @return The count.
*/
[[nodiscard]] constexpr size_t count() const noexcept {
return used_ + this->get_used_count();
}
/**
* Flushes the internal cache to the primary buffer.
*/
[[nodiscard]] constexpr result<void> flush() noexcept {
size_t bytes_to_write = get_used_count();
used_ += bytes_to_write;
while (written_ < limit_ && bytes_to_write > 0) {
EMIO_TRY(const auto area, primary_.get_write_area_of_max(std::min(bytes_to_write, limit_ - written_)));
detail::copy_n(detail::begin(cache_), area.size(), area.data());
written_ += area.size();
bytes_to_write -= area.size();
}
this->set_write_area(cache_);
return success;
}
protected:
constexpr result<std::span<char>> request_write_area(const size_t /*used*/, const size_t size) noexcept override {
EMIO_TRYV(flush());
const std::span<char> area{cache_};
this->set_write_area(area);
if (size > cache_.size()) {
return area;
}
return area.subspan(0, size);
}
private:
buffer& primary_;
size_t limit_;
size_t written_{};
size_t used_{};
std::array<char, CacheSize> cache_;
};
namespace detail {
/**
* A buffer that counts the number of characters written. Discards the output.
* @tparam CacheSize The size of the internal cache.
*/
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized.
template <size_t CacheSize = default_cache_size>
class counting_buffer final : public buffer {
public:
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): cache_ can be left uninitialized.
constexpr counting_buffer() noexcept = default;
constexpr counting_buffer(const counting_buffer&) = delete;
constexpr counting_buffer(counting_buffer&&) noexcept = delete;
constexpr counting_buffer& operator=(const counting_buffer&) = delete;
constexpr counting_buffer& operator=(counting_buffer&&) noexcept = delete;
constexpr ~counting_buffer() noexcept override = default;
/**
* Calculates the number of Char's that were written.
* @return The number of Char's.
*/
[[nodiscard]] constexpr size_t count() const noexcept {
return used_ + this->get_used_count();
}
protected:
constexpr result<std::span<char>> request_write_area(const size_t used, const size_t size) noexcept override {
used_ += used;
const std::span<char> area{cache_};
this->set_write_area(area);
if (size > cache_.size()) {
return area;
}
return area.subspan(0, size);
}
private:
size_t used_{};
std::array<char, CacheSize> cache_;
};
} // namespace detail
} // namespace emio
================================================
FILE: include/emio/detail/args.hpp
================================================
//
// Copyright (c) 2023 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include <span>
#include <string_view>
#include <type_traits>
#include "validated_string_storage.hpp"
namespace emio::detail {
/**
* Type erased argument to validate.
*/
template <template <typename> typename Trait>
class validation_arg {
public:
template <typename T>
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): will be initialized in constructor
explicit validation_arg(std::type_identity<T> /*unused*/) noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): only way to use the storage
std::construct_at(reinterpret_cast<model_t<typename Trait<T>::unified_type>*>(&storage_));
}
validation_arg(const validation_arg&) = delete;
validation_arg(validation_arg&&) = delete;
validation_arg& operator=(const validation_arg&) = delete;
validation_arg& operator=(validation_arg&&) = delete;
// No destructor & delete call to concept_t because model_t holds only a reference.
~validation_arg() = default;
result<void> validate(reader& format_rdr) const noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): only way to get the object back
return reinterpret_cast<const concept_t*>(&storage_)->validate(format_rdr);
}
private:
class concept_t {
public:
concept_t() = default;
concept_t(const concept_t&) = delete;
concept_t(concept_t&&) = delete;
concept_t& operator=(const concept_t&) = delete;
concept_t& operator=(concept_t&&) = delete;
virtual result<void> validate(reader& format_rdr) const noexcept = 0;
protected:
~concept_t() = default;
};
template <typename T>
class model_t final : public concept_t {
public:
explicit model_t() noexcept = default;
model_t(const model_t&) = delete;
model_t(model_t&&) = delete;
model_t& operator=(const model_t&) = delete;
model_t& operator=(model_t&&) = delete;
result<void> validate(reader& format_rdr) const noexcept override {
return Trait<std::remove_cvref_t<T>>::validate(format_rdr);
}
protected:
~model_t() = default;
};
std::aligned_storage_t<sizeof(model_t<int>)> storage_;
};
/**
* Type erased argument to parse and process.
*/
template <typename Input, template <typename> typename Trait>
class arg {
public:
template <typename T>
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): will be initialized in constructor
explicit arg(T& value) noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): only way to use the storage
std::construct_at(reinterpret_cast<model_t<typename Trait<T>::unified_type>*>(&storage_), value);
}
arg(const arg&) = delete;
arg(arg&&) = delete;
arg& operator=(const arg&) = delete;
arg& operator=(arg&&) = delete;
~arg() = default; // No destructor & delete call to concept_t because model_t holds only a reference.
result<void> process_arg(Input& input, reader& format_rdr) const noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast): only way to get the object back
return reinterpret_cast<const concept_t*>(&storage_)->process_arg(input, format_rdr);
}
private:
class concept_t {
public:
concept_t() = default;
concept_t(const concept_t&) = delete;
concept_t(concept_t&&) = delete;
concept_t& operator=(const concept_t&) = delete;
concept_t& operator=(concept_t&&) = delete;
virtual result<void> process_arg(Input& input, reader& format_rdr) const noexcept = 0;
protected:
~concept_t() = default;
};
template <typename T>
class model_t final : public concept_t {
public:
explicit model_t(T value) noexcept : value_{value} {}
model_t(const model_t&) = delete;
model_t(model_t&&) = delete;
model_t& operator=(const model_t&) = delete;
model_t& operator=(model_t&&) = delete;
result<void> process_arg(Input& input, reader& format_rdr) const noexcept override {
return Trait<std::remove_cvref_t<T>>::process_arg(input, format_rdr, value_);
}
protected:
~model_t() = default;
private:
T value_;
};
std::aligned_storage_t<sizeof(model_t<std::string_view>)> storage_;
};
template <typename Arg>
class args_span {
public:
args_span() = default;
args_span(const args_span&) = delete;
args_span(args_span&&) = delete;
args_span& operator=(const args_span&) = delete;
args_span& operator=(args_span&&) = delete;
~args_span() = default;
[[nodiscard]] std::span<const Arg> get_args() const noexcept {
return args_;
}
protected:
args_span(std::span<const Arg> args) : args_{args} {}
private:
std::span<const Arg> args_{};
};
template <typename Arg>
class args_span_with_str : public args_span<Arg> {
public:
args_span_with_str() = default;
args_span_with_str(const args_span_with_str&) = delete;
args_span_with_str(args_span_with_str&&) = delete;
args_span_with_str& operator=(const args_span_with_str&) = delete;
args_span_with_str& operator=(args_span_with_str&&) = delete;
~args_span_with_str() = default;
/**
* Returns the validated format/scan string.
* @return The view or invalid_format if the validation failed.
*/
[[nodiscard]] result<std::string_view> get_str() const noexcept {
return str_.get();
}
/**
* Returns if it is just a plain string without arguments.
* @return True, if the string does not contain any escape sequences, replacement fields or arguments, otherwise
* false.
*/
[[nodiscard]] constexpr bool is_plain_str() const noexcept {
return str_.is_plain_str();
}
/**
* Returns if it is an empty string without arguments.
* @return True, if the string is empty without any arguments, otherwise false.
*/
[[nodiscard]] constexpr bool empty() const noexcept {
return str_.empty();
}
protected:
// NOLINTNEXTLINE(modernize-pass-by-value): false-positive since no dynamic allocation takes place
args_span_with_str(const validated_string_storage& str, std::span<const Arg> args)
: args_span<Arg>(args), str_{str} {}
private:
validated_string_storage str_{};
};
template <typename Arg, size_t NbrOfArgs>
class args_storage : public args_span_with_str<Arg> {
public:
template <typename... Args>
// NOLINTNEXTLINE(modernize-pass-by-value): false-positive since no dynamic allocation takes place
args_storage(const validated_string_storage& str, Args&&... args) noexcept
: args_span_with_str<Arg>{str, args_storage_}, args_storage_{Arg{std::forward<Args>(args)}...} {}
args_storage(const args_storage&) = delete;
args_storage(args_storage&&) = delete;
args_storage& operator=(const args_storage&) = delete;
args_storage& operator=(args_storage&&) = delete;
~args_storage() = default;
private:
std::array<Arg, NbrOfArgs> args_storage_;
};
template <typename Arg, size_t NbrOfArgs>
class validation_args_storage : public args_span<Arg> {
public:
template <typename... Args>
validation_args_storage(Args&&... args) noexcept
: args_span<Arg>{args_storage_}, args_storage_{Arg{std::forward<Args>(args)}...} {}
validation_args_storage(const validation_args_storage&) = delete;
validation_args_storage(validation_args_storage&&) = delete;
validation_args_storage& operator=(const validation_args_storage&) = delete;
validation_args_storage& operator=(validation_args_storage&&) = delete;
~validation_args_storage() = default;
private:
std::array<Arg, NbrOfArgs> args_storage_;
};
template <typename T, typename... Args>
validation_args_storage<T, sizeof...(Args)> make_validation_args() noexcept {
return {std::type_identity<Args>{}...};
}
} // namespace emio::detail
================================================
FILE: include/emio/detail/bignum.hpp
================================================
//
// Copyright (c) 2023 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
// This implementation is based on:
// https://github.com/rust-lang/rust/blob/71ef9ecbdedb67c32f074884f503f8e582855c2f/library/core/src/num/bignum.rs
#pragma once
#include <algorithm>
#include <array>
#include <cstdint>
#include <exception>
#include <span>
#include "predef.hpp"
namespace emio::detail {
struct carrying_add_result_t {
uint32_t value;
bool carry;
friend constexpr bool operator==(const carrying_add_result_t& lhs,
const carrying_add_result_t& rhs) noexcept = default;
};
inline constexpr carrying_add_result_t carrying_add(uint32_t a, uint32_t b, bool carry) noexcept {
const uint32_t v1 = a + b;
const bool carry1 = v1 < a;
const uint32_t v2 = v1 + static_cast<uint32_t>(carry);
const bool carry2 = v2 < v1;
return {v2, carry1 || carry2};
}
struct borrowing_sub_result_t {
uint32_t value;
bool borrow;
friend constexpr bool operator==(const borrowing_sub_result_t& lhs,
const borrowing_sub_result_t& rhs) noexcept = default;
};
inline constexpr borrowing_sub_result_t borrowing_sub(uint32_t a, uint32_t b, bool borrow) noexcept {
const uint32_t v1 = a - b;
const bool borrow1 = v1 > a;
const uint32_t v2 = v1 - static_cast<uint32_t>(borrow);
const bool borrow2 = v2 > v1;
return {v2, borrow1 || borrow2};
}
struct carrying_mul_result_t {
uint32_t value;
uint32_t carry;
friend constexpr bool operator==(const carrying_mul_result_t& lhs,
const carrying_mul_result_t& rhs) noexcept = default;
};
inline constexpr carrying_mul_result_t carrying_mul(uint32_t a, uint32_t b, uint32_t carry) noexcept {
const uint64_t v1 = static_cast<uint64_t>(a) * b + carry;
const auto v2 = static_cast<uint32_t>(v1);
const auto carry1 = static_cast<uint32_t>(v1 >> 32U);
return {v2, carry1};
}
/// Stack-allocated arbitrary-precision (up to certain limit) integer.
class bignum {
public:
static constexpr size_t max_blocks = 34;
static constexpr bignum from(size_t sz, const std::array<uint32_t, max_blocks>& b) noexcept {
bignum bn{};
bn.size_ = sz;
bn.base_ = b;
return bn;
}
constexpr explicit bignum() noexcept = default;
/// Makes a bignum from one digit.
template <typename T>
requires(std::is_unsigned_v<T> && sizeof(T) <= sizeof(uint32_t))
constexpr explicit bignum(T v) noexcept : base_{{v}} {}
/// Makes a bignum from `u64` value.
template <typename T>
requires(std::is_unsigned_v<T> && sizeof(T) == sizeof(uint64_t))
constexpr explicit bignum(T v) noexcept : base_{{static_cast<uint32_t>(v), static_cast<uint32_t>(v >> 32)}} {
size_ += static_cast<size_t>(base_[1] > 0);
}
/// Returns the internal digits as a slice `[a, b, c, ...]` such that the numeric
/// value is `a + b * 2^W + c * 2^(2W) + ...` where `W` is the number of bits in
/// the digit type.
constexpr std::span<uint32_t> digits() noexcept {
return {base_.data(), size_};
}
/// Returns the `i`-th bit where bit 0 is the least significant one.
/// In other words, the bit with weight `2^i`.
[[nodiscard]] constexpr uint8_t get_bit(size_t i) const noexcept {
const size_t digitbits = 32;
const auto d = i / digitbits;
const auto b = i % digitbits;
return static_cast<uint8_t>((base_[d] >> b) & 1U);
}
/// Returns `true` if the bignum is zero.
[[nodiscard]] constexpr bool is_zero() const noexcept {
return std::all_of(base_.begin(), base_.end(), [](uint32_t v) {
return v == 0;
});
}
// add
// add_small
constexpr bignum& add_small(uint32_t other) noexcept {
return add_small_at(0, other);
}
constexpr bignum& add_small_at(size_t index, uint32_t other) noexcept {
size_t i = index;
auto res = carrying_add(base_[i], other, false);
base_[i] = res.value;
i += 1;
for (; res.carry && (i < base_.size()); i++) {
res = carrying_add(base_[i], 0, res.carry);
base_[i] = res.value;
}
EMIO_Z_DEV_ASSERT(!res.carry);
size_ = i;
return *this;
}
constexpr bignum& add(const bignum& other) noexcept {
carrying_add_result_t res{0, false};
size_t i = 0;
for (; (i < other.size_) || (res.carry && (i < base_.size())); i++) {
res = carrying_add(base_[i], other.base_[i], res.carry);
base_[i] = res.value;
}
EMIO_Z_DEV_ASSERT(!res.carry);
if (i > size_) {
size_ = i;
}
return *this;
}
/// Subtracts `other` from itself and returns its own mutable reference.
constexpr bignum& sub_small(uint32_t other) noexcept {
auto res = borrowing_sub(base_[0], other, false);
base_[0] = res.value;
size_t i = 1;
for (; res.borrow && (i < base_.size()); i++) {
res = borrowing_sub(base_[i], 0, res.borrow);
base_[i] = res.value;
}
EMIO_Z_DEV_ASSERT(!res.borrow);
if (i == size_ && size_ != 1) {
size_ -= 1;
}
return *this;
}
/// Subtracts `other` from itself and returns its own mutable reference.
constexpr bignum& sub(const bignum& other) noexcept {
EMIO_Z_DEV_ASSERT(size_ >= other.size_);
if (size_ == 0) {
return *this;
}
borrowing_sub_result_t res{0, false};
for (size_t i = 0; i < size_; i++) {
res = borrowing_sub(base_[i], other.base_[i], res.borrow);
base_[i] = res.value;
}
EMIO_Z_DEV_ASSERT(!res.borrow);
do {
if (base_[size_ - 1] != 0) {
break;
}
} while (--size_ != 0);
return *this;
}
/// Multiplies itself by a digit-sized `other` and returns its own
/// mutable reference.
constexpr bignum& mul_small(uint32_t other) noexcept {
return muladd_small(other, 0);
}
constexpr bignum& muladd_small(uint32_t other, uint32_t carry) noexcept {
carrying_mul_result_t res{0, carry};
for (size_t i = 0; i < size_; i++) {
res = carrying_mul(base_[i], other, res.carry);
base_[i] = res.value;
}
if (res.carry > 0) {
base_[size_] = res.carry;
size_ += 1;
}
return *this;
}
[[nodiscard]] bignum mul(const bignum& other) const noexcept {
const auto& bn_max = size_ > other.size_ ? *this : other;
const auto& bn_min = size_ > other.size_ ? other : *this;
bignum prod{};
for (size_t i = 0; i < bn_min.size_; i++) {
carrying_mul_result_t res{0, 0};
for (size_t j = 0; j < bn_max.size_; j++) {
res = carrying_mul(bn_min.base_[i], bn_max.base_[j], res.carry);
prod.add_small_at(i + j, res.value);
}
if (res.carry > 0) {
prod.add_small_at(i + bn_max.size_, res.carry);
}
}
return prod;
}
constexpr bignum& mul_digits(std::span<const uint32_t> other) noexcept {
const auto& bn_max = size_ > other.size() ? digits() : other;
const auto& bn_min = size_ > other.size() ? other : digits();
bignum prod{};
for (size_t i = 0; i < bn_min.size(); i++) {
carrying_mul_result_t res{0, 0};
for (size_t j = 0; j < bn_max.size(); j++) {
res = carrying_mul(bn_min[i], bn_max[j], res.carry);
prod.add_small_at(i + j, res.value);
}
if (res.carry > 0) {
prod.add_small_at(i + bn_max.size(), res.carry);
}
}
*this = prod;
return *this;
}
/// Multiplies itself by `5^e` and returns its own mutable reference.
constexpr bignum& mul_pow5(size_t k) noexcept {
// Multiply with the largest single-digit power as long as possible.
while (k >= 13) {
mul_small(1220703125);
k -= 13;
}
// Stop if nothing left.
if (k == 0) {
return *this;
}
// Finish off the remainder.
uint32_t rest_power{5};
while (--k > 0) {
rest_power *= 5;
}
return mul_small(rest_power);
}
/// Divides itself by a digit-sized `other` and returns its own
/// mutable reference *and* the remainder.
constexpr uint32_t div_rem_small(uint32_t other) noexcept {
uint64_t borrow = 0;
for (size_t i = size_; i > 0; i--) {
const uint64_t v = (base_[i - 1] + (borrow << 32U));
const uint64_t res = v / other;
base_[i - 1] = static_cast<uint32_t>(res);
borrow = v - res * other;
}
return static_cast<uint32_t>(borrow);
}
/// Multiplies itself by `2^exp` and returns its own mutable reference.
constexpr bignum& mul_pow2(size_t exp) noexcept {
const size_t digits = exp / 32;
const size_t bits = exp % 32;
if (digits > 0) {
for (size_t i = size_; i > 0; --i) {
base_[i + digits - 1] = base_[i - 1];
}
for (size_t i = 0; i < digits; i++) {
base_[i] = 0;
}
size_ += digits;
}
if (bits > 0) {
uint32_t overflow = 0;
size_t i = 0;
for (; i < size_; i++) {
auto res = static_cast<uint64_t>(base_[i]) << bits;
base_[i] = static_cast<uint32_t>(res) + overflow;
overflow = static_cast<uint32_t>(res >> 32);
}
if (overflow > 0) {
base_[i] = overflow;
size_ += 1;
}
}
return *this;
}
[[nodiscard]] constexpr std::strong_ordering operator<=>(const bignum& other) const noexcept {
if (size_ > other.size_) {
return std::strong_ordering::greater;
}
if (size_ < other.size_) {
return std::strong_ordering::less;
}
for (size_t i = size_; i > 0; i--) {
if (base_[i - 1] > other.base_[i - 1]) {
return std::strong_ordering::greater;
}
if (base_[i - 1] < other.base_[i - 1]) {
return std::strong_ordering::less;
}
}
return std::strong_ordering::equal;
}
constexpr bool operator==(const bignum& other) const noexcept = default;
private:
/// Number of "digits" used in base_.
size_t size_{1};
/// Digits. `[a, b, c, ...]` represents `a + b*2^W + c*2^(2W) + ...`
/// where `W` is the number of bits in the digit type.
std::array<uint32_t, max_blocks> base_{};
};
} // namespace emio::detail
================================================
FILE: include/emio/detail/bitset.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include <array>
#include <bit>
#include <exception>
#include <limits>
namespace emio::detail {
/**
* A constexpr bitset with the bare minimum implementation.
* @tparam Bits The number of bits.
*/
template <size_t Bits>
class bitset {
private:
using word_t = size_t;
static constexpr size_t bits_per_word = sizeof(word_t) * 8;
static constexpr size_t number_of_words = (Bits / bits_per_word) + (((Bits % bits_per_word) == 0) ? 0 : 1);
public:
/**
* Checks if all bits are set to true.
* @return true if all bits are set to true, otherwise false
*/
[[nodiscard]] constexpr bool all() const noexcept {
if constexpr (Bits <= 0) {
return true;
} else {
for (size_t i = 0; i < number_of_words - 1; i++) {
if (words_[i] != ~word_t{0}) {
return false;
}
}
constexpr word_t high_word_mask = get_high_word_mask();
return words_[number_of_words - 1] == high_word_mask;
}
}
/**
* Checks if the first n bits are set to true.
* @param n - number of bits
* @return true if the first n bits are set to true, otherwise false
*/
[[nodiscard, gnu::noinline]] constexpr bool all_first(size_t n) const noexcept {
// Prevent inlining because of a GCC compiler-bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106921
if constexpr (Bits <= 0) {
return n == 0;
} else {
if (n > Bits) {
return false;
}
size_t i = 0;
for (; n > bits_per_word; n -= bits_per_word, i++) {
if (words_[i] != ~word_t{0}) { // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index): ensured by loop
return false;
}
}
word_t last_word = words_[i]; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index): ensured by loop
for (; n != 0; n--) {
if ((last_word & 1) != 1) {
return false;
}
last_word >>= 1;
}
return true;
}
}
/**
* Returns the number of bits that the bitset holds.
* @return the number of bits
*/
[[nodiscard]] constexpr size_t size() const noexcept {
return Bits;
}
/**
* Sets a specific bit to true.
* @param pos - the position of the bit
*/
constexpr void set(size_t pos) noexcept {
if (pos >= Bits) {
std::terminate();
}
// Get index of pos in words and truncate pos to word bits per word.
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index): ensured by check before
words_[pos / bits_per_word] |= word_t{1} << (pos % bits_per_word);
}
private:
static constexpr word_t get_high_word_mask() noexcept {
word_t high_word_mask = (word_t{1} << (Bits % (bits_per_word))) - word_t{1};
if (high_word_mask == 0) {
return std::numeric_limits<word_t>::max();
}
return high_word_mask;
}
std::array<word_t, number_of_words> words_{};
};
} // namespace emio::detail
================================================
FILE: include/emio/detail/conversion.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include <bit>
#include <cstdint>
#include <cstring>
#include <limits>
#include <optional>
#include <string_view>
#include <type_traits>
#include "predef.hpp"
namespace emio::detail {
constexpr bool isalpha(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
constexpr bool isdigit(char c) {
return (c >= '0' && c <= '9');
}
constexpr bool is_valid_number_base(const int base) noexcept {
return base >= 2 && base <= 36;
}
constexpr std::optional<int> char_to_digit(const char c, const int base) noexcept {
if (c < '0') {
return std::nullopt;
}
int res{};
if (c >= 'a') {
res = c - 'a' + 10;
} else if (c >= 'A') {
res = c - 'A' + 10;
} else {
res = c - '0';
}
if (res < base) {
return res;
}
return std::nullopt;
}
constexpr char digit_to_char(const int digit, bool upper) noexcept {
if (digit >= 10) {
EMIO_Z_DEV_ASSERT(digit < 36);
if (upper) {
return static_cast<char>(static_cast<int>('A') + (digit - 10));
}
return static_cast<char>(static_cast<int>('a') + (digit - 10));
}
EMIO_Z_DEV_ASSERT(digit < 10);
return static_cast<char>(static_cast<int>('0') + digit);
}
template <typename T>
requires(std::is_unsigned_v<T>)
constexpr size_t count_digits_10(T number) noexcept {
size_t count = 1;
for (;;) {
// Integer division is slow so do it for a group of four digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++".
if (number < 10) {
return count;
}
if (number < 100) {
return count + 1;
}
if (number < 1000) {
return count + 2;
}
if (number < 10000) {
return count + 3;
}
number /= 10000U;
count += 4;
}
}
template <size_t Base, typename T>
requires(std::is_unsigned_v<T>)
constexpr size_t count_digits(T number) noexcept {
if (number == 0) {
return 1;
}
if constexpr (Base == 10) {
return count_digits_10(number);
} else if constexpr (Base == 2) {
return static_cast<size_t>(std::bit_width(number));
} else if constexpr (Base == 8) {
return static_cast<size_t>((std::bit_width(number) + 2) / 3);
} else if constexpr (Base == 16) {
return static_cast<size_t>(((std::bit_width(number) + 3) / 4));
} else {
size_t digit_cnt{1};
for (number /= static_cast<T>(Base); number; number /= static_cast<T>(Base)) {
++digit_cnt;
}
return digit_cnt;
}
}
template <typename T>
requires(std::is_unsigned_v<T>)
constexpr size_t get_number_of_digits(T number, int base) noexcept {
if (number == 0) {
return 1;
}
if (base == 10) {
return count_digits<10>(number);
}
if (base == 16) {
return count_digits<16>(number);
}
if (base == 2) {
return count_digits<2>(number);
}
if (base == 8) {
return count_digits<8>(number);
}
size_t digit_cnt{1};
for (number /= static_cast<T>(base); number; number /= static_cast<T>(base)) {
++digit_cnt;
}
return digit_cnt;
}
template <typename T>
constexpr bool is_negative(T value) noexcept {
if constexpr (std::is_signed_v<T>) {
return value < 0;
} else {
return false;
}
}
template <typename T>
constexpr int num_bits() noexcept {
return std::numeric_limits<T>::digits;
}
template <typename T>
using int32_or_64 = std::conditional_t<num_bits<T>() <= 32, int32_t, int64_t>;
template <typename T>
using uint32_or_64 = std::conditional_t<num_bits<T>() <= 32, uint32_t, uint64_t>;
template <typename T>
using upcasted_int_t = std::conditional_t<std::is_signed_v<T>, int32_or_64<T>, uint32_or_64<T>>;
template <typename T>
requires(std::is_integral_v<T>)
constexpr auto integer_upcast(T integer) noexcept {
return static_cast<upcasted_int_t<T>>(integer);
}
template <typename T>
constexpr uint32_or_64<T> to_absolute(T number) noexcept {
if constexpr (std::is_unsigned_v<T>) {
return number;
} else {
if (is_negative(number)) {
auto abs = static_cast<uint32_or_64<T>>(number);
abs = T{} - abs;
return abs;
}
return static_cast<uint32_or_64<T>>(number);
}
}
template <typename T>
constexpr std::make_unsigned_t<T> to_unsigned(T number) noexcept {
return static_cast<std::make_unsigned_t<T>>(number);
}
template <typename T>
constexpr std::make_signed_t<T> to_signed(T number) noexcept {
return static_cast<std::make_signed_t<T>>(number);
}
// Converts value in the range [0, 100) to a string.
inline constexpr const char* digits2(size_t value) noexcept {
// GCC generates slightly better code when value is pointer-size.
return &"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"[value * 2];
}
// Copies two characters from src to dst.
template <typename Char>
inline constexpr void copy2(Char* dst, const char* src) noexcept {
if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {
*dst++ = static_cast<Char>(*src++);
*dst = static_cast<Char>(*src);
} else {
memcpy(dst, src, 2);
}
}
template <typename T>
requires(std::is_unsigned_v<T>)
constexpr char* write_decimal(T abs_number, char* next) noexcept {
// Write number from right to left.
while (abs_number >= 100) {
next -= 2;
copy2(next, digits2(static_cast<size_t>(abs_number % 100)));
abs_number /= 100;
}
if (abs_number < 10) {
*--next = '0' + static_cast<char>(abs_number);
return next;
}
next -= 2;
copy2(next, digits2(static_cast<size_t>(abs_number)));
return next;
}
template <size_t BaseBits, typename T>
requires(std::is_unsigned_v<T>)
constexpr char* write_uint(T abs_number, const bool upper, char* next) noexcept {
const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
do {
T digit = static_cast<T>(abs_number & ((1 << BaseBits) - 1));
if constexpr (BaseBits < 4) {
EMIO_Z_DEV_ASSERT(digit < 8);
*--next = static_cast<char>('0' + digit);
} else {
EMIO_Z_DEV_ASSERT(digit < 16);
*--next = digits[digit];
}
} while ((abs_number >>= BaseBits) != 0);
return next;
}
template <typename T>
requires(std::is_unsigned_v<T>)
constexpr char* write_number(T abs_number, const int base, const bool upper, char* next) noexcept {
if (base == 10) {
return write_decimal(abs_number, next);
}
if (base == 16) {
return write_uint<4>(abs_number, upper, next);
}
if (base == 2) {
return write_uint<1>(abs_number, false, next);
}
if (base == 8) {
return write_uint<3>(abs_number, false, next);
}
if (abs_number == 0) {
*(--next) = '0';
return next;
}
// Write number from right to left.
for (; abs_number; abs_number /= static_cast<T>(base)) {
const char c = digit_to_char(static_cast<int>(abs_number % static_cast<T>(base)), upper);
*(--next) = c;
}
return next;
}
inline constexpr size_t npos = std::string_view::npos;
constexpr std::string_view unchecked_substr(const std::string_view& str, size_t pos, size_t n = npos) noexcept {
const size_t rlen = std::min(n, str.length() - pos);
return {str.data() + pos, rlen};
}
template <typename Size>
constexpr char* fill_n(char* out, Size count, char value) noexcept {
if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {
for (Size i = 0; i < count; i++) {
*out++ = value;
}
return out;
} else {
std::memset(out, value, to_unsigned(count));
return out + count;
}
}
template <typename Size>
constexpr char* copy_n(const char* in, Size count, char* out) noexcept {
if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {
for (Size i = 0; i < count; i++) {
*out++ = *in++;
}
return out;
} else {
std::memcpy(out, in, to_unsigned(count));
return out + count;
}
}
[[nodiscard]] inline constexpr bool equal_n(const char* a, const char* b, const size_t n) {
if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {
for (size_t i = 0; i < n; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
} else {
return std::memcmp(a, b, n) == 0;
}
}
using namespace std::string_view_literals;
// Helper function to construct string literals directly as string_view during compilation if string_view_literal
// operator "" sv is not available.
inline consteval std::string_view sv(std::string_view sv) noexcept {
return sv;
}
// T::iterator and T::const_iterator are not guaranteed to be pointers.
// But emio uses pointers to char* and const char*. Therefore, these helper functions are needed.
template <typename T>
concept is_iterable = requires(T&& t) {
t.begin() != t.end(); // begin/end and operator !=
++std::declval<decltype(t.begin())&>(); // operator ++
*t.begin(); // operator*
};
template <typename T>
requires is_iterable<T>
constexpr auto begin(T& obj) noexcept {
if constexpr (std::is_same_v<std::remove_pointer_t<typename T::iterator>, char>) {
return obj.begin();
} else {
return &*obj.begin();
}
}
template <typename T>
requires is_iterable<T>
constexpr auto end(T& obj) noexcept {
if constexpr (std::is_same_v<std::remove_pointer_t<typename T::iterator>, char>) {
return obj.end();
} else {
return &*obj.end();
}
}
template <typename T>
requires is_iterable<T>
constexpr const char* begin(const T& obj) noexcept {
if constexpr (std::is_same_v<std::remove_pointer_t<typename T::const_iterator>, char>) {
return obj.begin();
} else {
return &*obj.begin();
}
}
template <typename T>
requires is_iterable<T>
constexpr const char* end(const T& obj) noexcept {
if constexpr (std::is_same_v<std::remove_pointer_t<typename T::const_iterator>, char>) {
return obj.end();
} else {
return &*obj.end();
}
}
} // namespace emio::detail
================================================
FILE: include/emio/detail/ct_vector.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include <algorithm>
#include <array>
#include <cstddef>
#include "conversion.hpp"
#include "predef.hpp"
namespace emio::detail {
/**
* A constexpr vector with the bare minimum implementation and inlined storage.
* @tparam Char The character type.
* @tparam StorageSize The size of the inlined storage.
*/
template <typename Char, size_t StorageSize = 128>
class ct_vector {
public:
constexpr ct_vector() noexcept {
if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {
fill_n(storage_.data(), storage_.size(), 0);
}
}
constexpr ct_vector(const ct_vector& other) : ct_vector() {
reserve(other.size_);
copy_n(other.data_, other.size_, data_);
}
constexpr ct_vector(ct_vector&& other) noexcept : ct_vector() {
// Transfer ownership.
if (other.hold_external()) {
data_ = other.data_;
capacity_ = other.capacity_;
} else {
copy_n(other.data_, other.size_, data_);
}
size_ = other.size_;
// Reset other.
other.data_ = other.storage_.data();
other.size_ = 0;
other.capacity_ = StorageSize;
}
constexpr ct_vector& operator=(const ct_vector& other) {
if (&other == this) {
return *this;
}
reserve(other.size_);
copy_n(other.data_, other.size_, data_);
return *this;
}
constexpr ct_vector& operator=(ct_vector&& other) noexcept {
if (&other == this) {
return *this;
}
// Free this.
if (hold_external()) {
delete[] data_; // NOLINT(cppcoreguidelines-owning-memory)
}
// Transfer ownership.
if (other.hold_external()) {
data_ = other.data_;
capacity_ = other.capacity_;
} else {
copy_n(other.data_, other.size_, data_);
}
size_ = other.size_;
// Reset other.
other.data_ = other.storage_.data();
other.size_ = 0;
other.capacity_ = StorageSize;
return *this;
}
constexpr ~ct_vector() noexcept {
if (hold_external()) {
delete[] data_; // NOLINT(cppcoreguidelines-owning-memory)
}
}
constexpr void reserve(size_t new_size) noexcept {
if (new_size < StorageSize && !hold_external()) {
size_ = new_size;
return;
}
// Heavy pointer arithmetic because high level containers are not yet ready to use at constant evaluation.
if (capacity_ < new_size) {
// NOLINTNEXTLINE(bugprone-unhandled-exception-at-new): char types cannot throw
Char* new_data = new Char[new_size]; // NOLINT(cppcoreguidelines-owning-memory)
copy_n(data_, size_, new_data);
if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {
// Required at compile-time because another reserve could happen without previous write to the data.
fill_n(new_data + size_, new_size - size_, 0);
}
std::swap(new_data, data_);
capacity_ = new_size;
if (new_data != storage_.data()) {
delete[] new_data; // NOLINT(cppcoreguidelines-owning-memory)
}
}
size_ = new_size;
}
constexpr void clear() noexcept {
size_ = 0;
}
[[nodiscard]] constexpr size_t capacity() const noexcept {
return capacity_;
}
[[nodiscard]] constexpr size_t size() const noexcept {
return size_;
}
[[nodiscard]] constexpr Char* data() noexcept {
return data_;
}
[[nodiscard]] constexpr const Char* data() const noexcept {
return data_;
}
private:
[[nodiscard]] constexpr bool hold_external() const noexcept {
return data_ != storage_.data() && data_ != nullptr;
}
std::array<Char, StorageSize> storage_;
Char* data_{storage_.data()};
size_t size_{};
size_t capacity_{StorageSize};
};
} // namespace emio::detail
================================================
FILE: include/emio/detail/format/args.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include "../../formatter.hpp"
#include "../args.hpp"
namespace emio::detail::format {
template <typename Arg>
struct format_arg_trait {
using unified_type = format::unified_type_t<std::remove_const_t<Arg>>;
static constexpr result<void> validate(reader& format_rdr) noexcept {
return detail::format::validate_trait<Arg>(format_rdr);
}
static constexpr result<void> process_arg(writer& out, reader& format_rdr, const Arg& arg) noexcept {
formatter<Arg> formatter;
EMIO_TRYV(formatter.parse(format_rdr));
return formatter.format(out, arg);
}
};
using format_validation_arg = validation_arg<format_arg_trait>;
using format_arg = arg<writer, format_arg_trait>;
using format_args = args_span_with_str<format_arg>;
} // namespace emio::detail::format
================================================
FILE: include/emio/detail/format/decode.hpp
================================================
//
// Copyright (c) 2023 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
// This implementation is based on:
// https://github.com/rust-lang/rust/blob/71ef9ecbdedb67c32f074884f503f8e582855c2f/library/core/src/num/flt2dec/decoder.rs
#pragma once
#include <bit>
#include <cstdint>
#include <cstring>
#include <limits>
#include <type_traits>
namespace emio::detail::format {
struct finite_result_t {
uint64_t mant{};
uint64_t minus{};
uint64_t plus{};
int16_t exp{};
bool inclusive{};
};
enum class category { zero, finite, infinity, nan };
struct decode_result_t {
bool negative{};
format::category category{};
finite_result_t finite{}; // Only valid if category is finite.
};
inline constexpr decode_result_t decode(double value) noexcept {
decode_result_t res{};
using bits_type = uint64_t;
const auto bits = std::bit_cast<bits_type>(value);
res.negative = bits >> 63 != 0;
if (value == 0) {
return res;
}
// Exponent bias + mantissa shift
res.finite.exp = static_cast<int16_t>(((bits >> 52) & 0x7ff) - (1023 + 52));
res.finite.mant = res.finite.exp == -1075 ? (bits & 0xfffffffffffff) << 1 : (bits & 0xfffffffffffff);
res.finite.inclusive = (res.finite.mant & 1) == 0;
if (res.finite.exp == 972) { // non-numbers.
if (res.finite.mant == 0) {
res.category = category::infinity;
} else {
res.category = category::nan;
}
} else {
res.category = category::finite;
res.finite.minus = 1;
res.finite.plus = 1;
if (res.finite.exp != -1075) { // Norm.
res.finite.mant |= 0x10000000000000;
constexpr auto minnorm = std::bit_cast<bits_type>(std::numeric_limits<double>::min());
if (res.finite.mant == minnorm) {
res.finite.plus = 2;
res.finite.mant <<= 2;
res.finite.exp -= 2;
} else {
res.finite.mant <<= 1;
res.finite.exp -= 1;
}
}
}
return res;
}
} // namespace emio::detail::format
================================================
FILE: include/emio/detail/format/dragon.hpp
================================================
//
// Copyright (c) 2023 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
// This implementation is based on:
// https://github.com/rust-lang/rust/blob/71ef9ecbdedb67c32f074884f503f8e582855c2f/library/core/src/num/flt2dec/strategy/dragon.rs
#pragma once
#include <algorithm>
#include <bit>
#include <cstdint>
#include <cstring>
#include <limits>
#include <optional>
#include <span>
#include <type_traits>
#include "../../buffer.hpp"
#include "../bignum.hpp"
#include "decode.hpp"
namespace emio::detail::format {
inline constexpr int16_t estimate_scaling_factor(uint64_t mant, int16_t exp) noexcept {
// 2^(nbits-1) < mant <= 2^nbits if mant > 0
const int nbits = 64 - std::countl_zero(mant - 1);
// 1292913986 = floor(2^32 * log_10 2)
// therefore this always underestimates (or is exact), but not much.
return static_cast<int16_t>((static_cast<int64_t>(nbits + exp) * 1292913986) >> 32);
}
inline constexpr std::optional<char> round_up(std::span<char> d) noexcept {
const auto end = d.rend();
auto it = std::find_if(d.rbegin(), end, [](char c) {
return c != '9';
});
if (it != end) {
// d[i+1..n] is all nines.
auto i = static_cast<size_t>(std::distance(it, end) - 1);
d[i] += 1; // Round up.
for (size_t j = i + 1; j < d.size(); j++) {
d[j] = '0';
}
return std::nullopt;
} else if (!d.empty()) {
// 999..999 rounds to 1000..000 with an increased exponent
d[0] = '1';
for (char& c : d.subspan(1)) {
c = '0';
}
return '0';
}
// an empty buffer rounds up (a bit strange but reasonable)
return '1';
}
struct format_fp_result_t {
std::span<const char> digits;
int16_t exp;
};
enum class format_exact_mode { significand_digits, decimal_point };
inline constexpr format_fp_result_t format_exact(const finite_result_t& dec, emio::buffer& buf, format_exact_mode mode,
int16_t number_of_digits) noexcept {
EMIO_Z_DEV_ASSERT(dec.mant > 0);
EMIO_Z_DEV_ASSERT(dec.minus > 0);
EMIO_Z_DEV_ASSERT(dec.plus > 0);
// estimate `k_0` from original inputs satisfying `10^(k_0-1) < v <= 10^(k_0+1)`.
int16_t k = estimate_scaling_factor(dec.mant, dec.exp);
// `v = mant / scale`.
auto mant = bignum(dec.mant);
auto scale = bignum(1U);
size_t s2 = 0;
size_t s5 = 0;
size_t m2 = 0;
size_t m5 = 0;
if (dec.exp < 0) {
s2 = static_cast<size_t>(-dec.exp);
} else {
m2 += static_cast<size_t>(dec.exp);
}
// divide `mant` by `10^k`. now `scale / 10 < mant <= scale * 10`.
if (k >= 0) {
s2 += static_cast<size_t>(k);
s5 += static_cast<size_t>(k);
} else {
m2 += static_cast<size_t>(-k);
m5 += static_cast<size_t>(-k);
}
scale.mul_pow5(s5);
scale.mul_pow2(s2);
mant.mul_pow5(m5);
mant.mul_pow2(m2);
// calculate required buffer size
size_t len{};
size_t extra_len{};
if (mode == format_exact_mode::significand_digits) {
len = static_cast<size_t>(number_of_digits);
} else if ((k + number_of_digits) >= 0) {
len = static_cast<size_t>(k + number_of_digits);
extra_len = 1;
}
// fixup estimation
// in order to keep the fixed-size bignum, we actually use `mant + floor(plus) >= scale`.
// we are not actually modifying `scale`, since we can skip the initial multiplication instead.
// again with the shortest algorithm, `d[0]` can be zero but will be eventually rounded up.
// if we are working with the last-digit limitation, we need to shorten the buffer
// before the actual rendering in order to avoid double rounding.
// note that we have to enlarge the buffer again when rounding up happens!
if (mant >= scale) {
k += 1;
len += extra_len;
} else {
mant.mul_small(10);
}
auto dst = buf.get_write_area_of(len).value();
if (len > 0) {
// cache `(2, 4, 8) * scale` for digit generation.
bignum scale2 = scale;
scale2.mul_pow2(1);
bignum scale4 = scale;
scale4.mul_pow2(2);
bignum scale8 = scale;
scale8.mul_pow2(3);
for (size_t i = 0; i < len; i++) {
if (mant.is_zero()) {
// following digits are all zeroes, we stop here
// do *not* try to perform rounding! rather, fill remaining digits.
for (char& c : dst.subspan(i)) {
c = '0';
}
return {dst, k};
}
size_t d = 0;
if (mant >= scale8) {
mant.sub(scale8);
d += 8;
}
if (mant >= scale4) {
mant.sub(scale4);
d += 4;
}
if (mant >= scale2) {
mant.sub(scale2);
d += 2;
}
if (mant >= scale) {
mant.sub(scale);
d += 1;
}
EMIO_Z_DEV_ASSERT(mant < scale);
EMIO_Z_DEV_ASSERT(d < 10);
dst[i] = static_cast<char>('0' + d);
mant.mul_small(10);
}
}
// rounding up if we stop in the middle of digits
// if the following digits are exactly 5000..., check the prior digit and try to
// round to even (i.e., avoid rounding up when the prior digit is even).
const auto order = mant <=> (scale.mul_small(5));
if (order == std::strong_ordering::greater ||
(order == std::strong_ordering::equal && len > 0 && (dst[len - 1] & 1) == 1)) {
// if rounding up changes the length, the exponent should also change.
// but we've been requested a fixed number of digits, so do not alter the buffer...
if (std::optional<char> c = round_up(dst.subspan(0, len))) {
k += 1;
// ...unless we've been requested the fixed precision instead.
// we also need to check that, if the original buffer was empty,
// the additional digit can only be added when `k == limit` (edge case).
if (k > -number_of_digits) {
if (len == 0) {
dst = buf.get_write_area_of(1).value();
}
if (len != 0 && len < dst.size()) {
return {};
}
if (len < dst.size()) {
dst[len] = *c;
len += 1;
}
}
}
}
return {dst.subspan(0, len), k};
}
inline constexpr format_fp_result_t format_shortest(const finite_result_t& dec, emio::buffer& buf) noexcept {
// the number `v` to format is known to be:
// - equal to `mant * 2^exp`;
// - preceded by `(mant - 2 * minus) * 2^exp` in the original type; and
// - followed by `(mant + 2 * plus) * 2^exp` in the original type.
//
// obviously, `minus` and `plus` cannot be zero. (for infinities, we use out-of-range values.)
// also we assume that at least one digit is generated, i.e., `mant` cannot be zero too.
//
// this also means that any number between `low = (mant - minus) * 2^exp` and
// `high = (mant + plus) * 2^exp` will map to this exact floating point number,
// with bounds included when the original mantissa was even (i.e., `!mant_was_odd`).
EMIO_Z_DEV_ASSERT(dec.mant > 0);
EMIO_Z_DEV_ASSERT(dec.minus > 0);
EMIO_Z_DEV_ASSERT(dec.plus > 0);
// EMIO_Z_DEV_ASSERT(buf.() >= MAX_SIG_DIGITS);
// `a.cmp(&b) < rounding` is `if d.inclusive {a <= b} else {a < b}`
const auto rounding = [&](std::strong_ordering ordering) noexcept {
if (dec.inclusive) {
return ordering <= 0; // NOLINT(modernize-use-nullptr): false positive
}
return ordering < 0; // NOLINT(modernize-use-nullptr): false positive
};
// estimate `k_0` from original inputs satisfying `10^(k_0-1) < high <= 10^(k_0+1)`.
// the tight bound `k` satisfying `10^(k-1) < high <= 10^k` is calculated later.
int16_t k = estimate_scaling_factor(dec.mant + dec.plus, dec.exp);
// convert `{mant, plus, minus} * 2^exp` into the fractional form so that:
// - `v = mant / scale`
// - `low = (mant - minus) / scale`
// - `high = (mant + plus) / scale`
auto mant = bignum(dec.mant);
auto minus = bignum(dec.minus);
auto plus = bignum(dec.plus);
auto scale = bignum(1U);
size_t s2 = 0;
size_t s5 = 0;
size_t m2 = 0;
size_t m5 = 0;
if (dec.exp < 0) {
s2 = static_cast<size_t>(-dec.exp);
} else {
m2 += static_cast<size_t>(dec.exp);
}
// divide `mant` by `10^k`. now `scale / 10 < mant + plus <= scale * 10`.
if (k >= 0) {
s2 += static_cast<size_t>(k);
s5 += static_cast<size_t>(k);
} else {
m2 += static_cast<size_t>(-k);
m5 += static_cast<size_t>(-k);
}
scale.mul_pow5(s5);
scale.mul_pow2(s2);
mant.mul_pow5(m5);
mant.mul_pow2(m2);
minus.mul_pow5(m5);
minus.mul_pow2(m2);
plus.mul_pow5(m5);
plus.mul_pow2(m2);
// fixup when `mant + plus > scale` (or `>=`).
// we are not actually modifying `scale`, since we can skip the initial multiplication instead.
// now `scale < mant + plus <= scale * 10` and we are ready to generate digits.
//
// note that `d[0]` *can* be zero, when `scale - plus < mant < scale`.
// in this case rounding-up condition (`up` below) will be triggered immediately.
if (rounding(scale <=> (bignum{mant}.add(plus)))) {
// equivalent to scaling `scale` by 10
k += 1;
} else {
mant.mul_small(10);
minus.mul_small(10);
plus.mul_small(10);
}
// cache `(2, 4, 8) * scale` for digit generation.
bignum scale2 = scale;
scale2.mul_pow2(1);
bignum scale4 = scale;
scale4.mul_pow2(2);
bignum scale8 = scale;
scale8.mul_pow2(3);
auto dst = buf.get_write_area_of(std::numeric_limits<double>::max_digits10).value();
bool down{};
bool up{};
size_t i{};
while (true) {
// invariants, where `d[0..n-1]` are digits generated so far:
// - `v = mant / scale * 10^(k-n-1) + d[0..n-1] * 10^(k-n)`
// - `v - low = minus / scale * 10^(k-n-1)`
// - `high - v = plus / scale * 10^(k-n-1)`
// - `(mant + plus) / scale <= 10` (thus `mant / scale < 10`)
// where `d[i..j]` is a shorthand for `d[i] * 10^(j-i) + ... + d[j-1] * 10 + d[j]`.
// generate one digit: `d[n] = floor(mant / scale) < 10`.
size_t d = 0;
if (mant >= scale8) {
mant.sub(scale8);
d += 8;
}
if (mant >= scale4) {
mant.sub(scale4);
d += 4;
}
if (mant >= scale2) {
mant.sub(scale2);
d += 2;
}
if (mant >= scale) {
mant.sub(scale);
d += 1;
}
EMIO_Z_DEV_ASSERT(mant < scale);
EMIO_Z_DEV_ASSERT(d < 10);
dst[i] = static_cast<char>('0' + d);
i += 1;
// this is a simplified description of the modified Dragon algorithm.
// many intermediate derivations and completeness arguments are omitted for convenience.
//
// start with modified invariants, as we've updated `n`:
// - `v = mant / scale * 10^(k-n) + d[0..n-1] * 10^(k-n)`
// - `v - low = minus / scale * 10^(k-n)`
// - `high - v = plus / scale * 10^(k-n)`
//
// assume that `d[0..n-1]` is the shortest representation between `low` and `high`,
// i.e., `d[0..n-1]` satisfies both of the following but `d[0..n-2]` doesn't:
// - `low < d[0..n-1] * 10^(k-n) < high` (bijectivity: digits round to `v`); and
// - `abs(v / 10^(k-n) - d[0..n-1]) <= 1/2` (the last digit is correct).
//
// the second condition simplifies to `2 * mant <= scale`.
// solving invariants in terms of `mant`, `low` and `high` yields
// a simpler version of the first condition: `-plus < mant < minus`.
// since `-plus < 0 <= mant`, we have the correct shortest representation
// when `mant < minus` and `2 * mant <= scale`.
// (the former becomes `mant <= minus` when the original mantissa is even.)
//
// when the second doesn't hold (`2 * mant > scale`), we need to increase the last digit.
// this is enough for restoring that condition: we already know that
// the digit generation guarantees `0 <= v / 10^(k-n) - d[0..n-1] < 1`.
// in this case, the first condition becomes `-plus < mant - scale < minus`.
// since `mant < scale` after the generation, we have `scale < mant + plus`.
// (again, this becomes `scale <= mant + plus` when the original mantissa is even.)
//
// in short:
// - stop and round `down` (keep digits as is) when `mant < minus` (or `<=`).
// - stop and round `up` (increase the last digit) when `scale < mant + plus` (or `<=`).
// - keep generating otherwise.
down = rounding(mant <=> (minus));
up = rounding(scale <=> (bignum{mant}.add(plus)));
if (down || up) {
// we have the shortest representation, proceed to the rounding
break;
}
// restore the invariants.
// this makes the algorithm always terminating: `minus` and `plus` always increases,
// but `mant` is clipped modulo `scale` and `scale` is fixed.
mant.mul_small(10);
minus.mul_small(10);
plus.mul_small(10);
}
// rounding up happens when
// i) only the rounding-up condition was triggered, or
// ii) both conditions were triggered and tie breaking prefers rounding up.
if (up && (!down || mant.mul_pow2(1) >= scale)) {
// if rounding up changes the length, the exponent should also change.
// it seems that this condition is very hard to satisfy (possibly impossible),
// but we are just being safe and consistent here.
// SAFETY: we initialized that memory above.
if (std::optional<char> c = round_up(dst.subspan(0, i))) {
dst[i] = *c;
i += 1;
k += 1;
}
}
return {dst.subspan(0, i), k};
}
} // namespace emio::detail::format
================================================
FILE: include/emio/detail/format/format_to.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include "../../reader.hpp"
#include "../../writer.hpp"
#include "../validated_string.hpp"
#include "args.hpp"
#include "formatter.hpp"
#include "parser.hpp"
namespace emio {
namespace detail::format {
struct format_trait {
template <typename... Args>
[[nodiscard]] static constexpr bool validate_string(std::string_view format_str) noexcept {
if (EMIO_Z_INTERNAL_IS_CONST_EVAL) {
return validate<format_specs_checker>(format_str, sizeof...(Args), std::type_identity<Args>{}...);
} else {
return validate<format_specs_checker>(
format_str, sizeof...(Args), related_format_args{make_validation_args<format_validation_arg, Args...>()});
}
}
};
template <typename... Args>
using format_string = validated_string<format_trait, std::type_identity_t<Args>...>;
template <typename... Args>
using valid_format_string = valid_string<format_trait, std::type_identity_t<Args>...>;
// Non constexpr version.
inline result<void> vformat_to(buffer& buf, const format_args& args) noexcept {
EMIO_TRY(const std::string_view str, args.get_str());
writer wtr{buf};
if (args.is_plain_str()) {
return wtr.write_str(str);
}
return parse<format_parser>(str, wtr, related_format_args{args});
}
// Constexpr version.
template <typename... Args>
constexpr result<void> format_to(buffer& buf, const format_string<Args...>& format_string,
const Args&... args) noexcept {
EMIO_TRY(const std::string_view str, format_string.get());
writer wtr{buf};
if (format_string.is_plain_str()) {
return wtr.write_str(str);
}
return parse<format_parser>(str, wtr, args...);
}
} // namespace detail::format
/**
* Formatter for format_args.
*/
template <>
class formatter<detail::format::format_args> {
public:
static constexpr result<void> validate(reader& format_rdr) noexcept {
return format_rdr.read_if_match_char('}');
}
static constexpr result<void> parse(reader& format_rdr) noexcept {
return format_rdr.read_if_match_char('}');
}
static result<void> format(writer& out, const detail::format::format_args& arg) noexcept {
return detail::format::vformat_to(out.get_buffer(), arg);
}
static constexpr bool format_can_fail = true;
};
/**
* Formatter for types which inherit from format_args.
*/
template <typename T>
requires(std::is_base_of_v<detail::format::format_args, T>)
class formatter<T> : public formatter<detail::format::format_args> {};
namespace detail::format {
template <typename T>
requires(std::is_base_of_v<detail::format::format_args, T>)
struct unified_type<T> {
using type = const detail::format::format_args&;
};
} // namespace detail::format
} // namespace emio
================================================
FILE: include/emio/detail/format/formatter.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include "../../reader.hpp"
#include "../../writer.hpp"
#include "../misc.hpp"
#include "dragon.hpp"
#include "specs.hpp"
namespace emio {
template <typename>
class formatter;
namespace detail::format {
namespace alternate_form {
inline constexpr std::string_view bin_lower{"0b"};
inline constexpr std::string_view bin_upper{"0B"};
inline constexpr std::string_view octal{"0"};
inline constexpr std::string_view octal_lower{"0o"};
inline constexpr std::string_view octal_upper{"0O"};
inline constexpr std::string_view hex_lower{"0x"};
inline constexpr std::string_view hex_upper{"0X"};
} // namespace alternate_form
//
// Write args.
//
inline constexpr result<void> write_padding_left(writer& out, format_specs& specs, size_t width) noexcept {
if (specs.width == 0 || specs.width < static_cast<int>(width)) {
specs.width = 0;
return success;
}
int fill_width = specs.width - static_cast<int>(width);
if (specs.align == alignment::left) {
specs.width = fill_width;
return success;
}
if (specs.align == alignment::center) {
fill_width = fill_width / 2;
}
specs.width -= fill_width + static_cast<int>(width);
return out.write_char_n(specs.fill, static_cast<size_t>(fill_width));
}
inline constexpr result<void> write_padding_right(writer& out, format_specs& specs) noexcept {
if (specs.width == 0 || (specs.align != alignment::left && specs.align != alignment::center)) {
return success;
}
return out.write_char_n(specs.fill, static_cast<size_t>(specs.width));
}
template <alignment DefaultAlign, typename Func>
constexpr result<void> write_padded(writer& out, format_specs& specs, size_t width, const Func& func) noexcept {
if (specs.align == alignment::none) {
specs.align = DefaultAlign;
}
EMIO_TRYV(write_padding_left(out, specs, width));
EMIO_TRYV(func());
return write_padding_right(out, specs);
}
inline constexpr result<std::pair<std::string_view, writer::write_int_options>> make_write_int_options(
char spec_type) noexcept {
using namespace alternate_form;
std::string_view prefix;
writer::write_int_options options{};
switch (spec_type) {
case no_type:
case 'd':
options.base = 10;
break;
case 'x':
prefix = hex_lower;
options.base = 16;
break;
case 'X':
prefix = hex_upper;
options = {.base = 16, .upper_case = true};
break;
case 'b':
prefix = bin_lower;
options.base = 2;
break;
case 'B':
prefix = bin_upper;
options.base = 2;
break;
case 'o':
prefix = octal;
options.base = 8;
break;
default:
return err::invalid_format;
}
return std::pair{prefix, options};
}
inline constexpr result<char> try_write_sign(writer& out, const format_specs& specs, bool is_negative) noexcept {
char sign_to_write = no_sign;
if (is_negative) {
sign_to_write = '-';
} else if (specs.sign == '+' || specs.sign == ' ') {
sign_to_write = specs.sign;
}
if (sign_to_write != no_sign && specs.zero_flag) {
EMIO_TRYV(out.write_char(sign_to_write));
return no_sign;
}
return sign_to_write;
}
inline constexpr result<std::string_view> try_write_prefix(writer& out, const format_specs& specs,
std::string_view prefix) noexcept {
const bool write_prefix = specs.alternate_form && !prefix.empty();
if (write_prefix && specs.zero_flag) {
EMIO_TRYV(out.write_str(prefix));
return ""sv;
}
if (write_prefix) {
return prefix;
}
return ""sv;
}
template <typename Arg>
requires(std::is_integral_v<Arg> && !std::is_same_v<Arg, bool> && !std::is_same_v<Arg, char>)
constexpr result<void> write_arg(writer& out, format_specs& specs, const Arg& arg) noexcept {
if (specs.type == 'c') {
return write_padded<alignment::left>(out, specs, 1, [&]() noexcept {
return out.write_char(static_cast<char>(arg));
});
}
EMIO_TRY((auto [prefix, options]), make_write_int_options(specs.type));
if (specs.type == 'o' && arg == 0) {
prefix = ""sv;
}
const auto abs_number = detail::to_absolute(arg);
const bool is_negative = detail::is_negative(arg);
const size_t num_digits = detail::get_number_of_digits(abs_number, options.base);
EMIO_TRY(const char sign_to_write, try_write_sign(out, specs, is_negative));
EMIO_TRY(const std::string_view prefix_to_write, try_write_prefix(out, specs, prefix));
size_t total_width = num_digits;
if (specs.alternate_form) {
total_width += prefix.size();
}
if (is_negative || specs.sign == ' ' || specs.sign == '+') {
total_width += 1;
}
return write_padded<alignment::right>(out, specs, total_width, [&, &opt = options]() noexcept -> result<void> {
const size_t area_size = num_digits + static_cast<size_t>(sign_to_write != no_sign) + prefix_to_write.size();
EMIO_TRY(auto area, out.get_buffer().get_write_area_of(area_size));
auto* it = area.data();
if (sign_to_write != no_sign) {
*it++ = sign_to_write;
}
if (!prefix_to_write.empty()) {
it = copy_n(prefix_to_write.data(), prefix_to_write.size(), it);
}
write_number(abs_number, opt.base, opt.upper_case, it + detail::to_signed(num_digits));
return success;
});
}
inline constexpr result<void> write_non_finite(writer& out, bool upper_case, bool is_inf) noexcept {
if (is_inf) {
EMIO_TRYV(out.write_str(upper_case ? "INF"sv : "inf"sv));
} else {
EMIO_TRYV(out.write_str(upper_case ? "NAN"sv : "nan"sv));
}
return success;
}
// A floating-point presentation format.
enum class fp_format : uint8_t {
general, // General: exponent notation or fixed point based on magnitude.
exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
hex
};
struct fp_format_specs {
int16_t precision;
fp_format format;
bool upper_case;
bool showpoint;
};
inline constexpr fp_format_specs parse_fp_format_specs(const format_specs& specs) noexcept {
constexpr int16_t default_precision = 6;
// This spec is typically for general format.
fp_format_specs fp_specs{
.precision =
specs.precision >= 0 || specs.type == no_type ? static_cast<int16_t>(specs.precision) : default_precision,
.format = fp_format::general,
.upper_case = specs.type == 'E' || specs.type == 'F' || specs.type == 'G',
.showpoint = specs.alternate_form,
};
if (specs.type == 'e' || specs.type == 'E') {
fp_specs.format = fp_format::exp;
fp_specs.precision += 1;
fp_specs.showpoint |= specs.precision != 0;
} else if (specs.type == 'f' || specs.type == 'F') {
fp_specs.format = fp_format::fixed;
fp_specs.showpoint |= specs.precision != 0;
} else if (specs.type == 'a' || specs.type == 'A') {
fp_specs.format = fp_format::hex;
}
if (fp_specs.format != fp_format::fixed && fp_specs.precision == 0) {
fp_specs.precision = 1; // Calculate at least on significand.
}
return fp_specs;
}
inline constexpr char* write_significand(char* out, const char* significand, int significand_size, int integral_size,
char decimal_point) noexcept {
out = copy_n(significand, integral_size, out);
if (decimal_point == 0) {
return out;
}
*out++ = decimal_point;
return copy_n(significand + integral_size, significand_size - integral_size, out);
}
inline constexpr char* write_exponent(char* it, int exp) noexcept {
if (exp < 0) {
*it++ = '-';
exp = -exp;
} else {
*it++ = '+';
}
int cnt = 2;
if (exp >= 100) {
write_decimal(to_unsigned(exp), it + 3);
return it;
} else if (exp < 10) {
*it++ = '0';
cnt -= 1;
}
write_decimal(to_unsigned(exp), it + cnt);
return it;
}
inline constexpr result<void> write_decimal(writer& out, format_specs& specs, fp_format_specs& fp_specs,
bool is_negative, const format_fp_result_t& f) noexcept {
const char* significand = f.digits.data();
int significand_size = static_cast<int>(f.digits.size());
const int output_exp = f.exp - 1; // 0.1234 x 10^exp => 1.234 x 10^(exp-1)
const int abs_output_exp = static_cast<uint16_t>(output_exp >= 0 ? output_exp : -output_exp);
const bool has_sign = is_negative || specs.sign == ' ' || specs.sign == '+';
if (fp_specs.format == fp_format::general && significand_size > 1) {
// Remove trailing zeros.
auto it = std::find_if(f.digits.rbegin(), f.digits.rend(), [](char c) {
return c != '0';
});
significand_size -= static_cast<int>(it - f.digits.rbegin());
}
const auto use_exp_format = [=]() noexcept {
if (fp_specs.format == fp_format::exp) {
return true;
}
if (fp_specs.format != fp_format::general) {
return false;
}
// Use the fixed notation if the exponent is in [exp_lower, exp_upper),
// e.g. 0.0001 instead of 1e-04. Otherwise, use the exponent notation.
constexpr int exp_lower = -4;
constexpr int exp_upper = 16;
return output_exp < exp_lower || output_exp >= (fp_specs.precision > 0 ? fp_specs.precision : exp_upper);
};
EMIO_TRY(const char sign_to_write, try_write_sign(out, specs, is_negative));
int num_zeros = 0;
char decimal_point = '.';
size_t total_width = static_cast<uint32_t>(has_sign);
size_t num_digits = to_unsigned(significand_size);
if (use_exp_format()) {
if (fp_specs.showpoint) { // Multiple significands or high precision.
num_zeros = fp_specs.precision - significand_size; // Trailing zeros after an zero only.
if (num_zeros < 0) {
num_zeros = 0;
}
num_digits += to_unsigned(num_zeros);
} else if (significand_size == 1) { // One significand.
decimal_point = 0;
}
// The else part is general format with significand size less than the exponent.
const int exp_digits = abs_output_exp >= 100 ? 3 : 2;
num_digits += to_unsigned((decimal_point != 0 ? 1 : 0) + 2 /* sign + e */ + exp_digits);
total_width += num_digits;
return write_padded<alignment::right>(out, specs, total_width, [&]() noexcept -> result<void> {
const size_t area_size = num_digits + static_cast<size_t>(sign_to_write != no_sign);
EMIO_TRY(auto area, out.get_buffer().get_write_area_of(area_size));
auto* it = area.data();
if (sign_to_write != no_sign) {
*it++ = sign_to_write;
}
it = write_significand(it, significand, significand_size, 1, decimal_point);
it = fill_n(it, num_zeros, '0');
*it++ = fp_specs.upper_case ? 'E' : 'e';
write_exponent(it, output_exp);
return success;
});
}
int integral_size = 0;
int num_zeros_2 = 0;
if (output_exp < 0) { // Only fractional-part.
num_digits += 2; // For zero + Decimal point.
num_zeros = abs_output_exp - 1; // Leading zeros after dot.
if (specs.alternate_form && fp_specs.format == fp_format::general) { // ({:#g}, 0.1) -> 0.100000 instead 0.1
num_zeros_2 = fp_specs.precision - significand_size;
}
} else if ((output_exp + 1) >= significand_size) { // Only integer-part (including zero).
integral_size = significand_size;
num_zeros = output_exp - significand_size + 1; // Trailing zeros.
if (fp_specs.showpoint) { // Significand is zero but fractional requested.
if (specs.alternate_form && fp_specs.format == fp_format::general) { // ({:#.4g}, 1) -> 1.000 instead of 1.
num_zeros_2 = fp_specs.precision - significand_size - num_zeros;
} else if (num_zeros == 0) { // ({:f}, 0) or ({:.4f}, 1.23e-06) -> 0.000000 instead of 0
num_zeros_2 = fp_specs.precision;
}
EMIO_Z_DEV_ASSERT(num_zeros >= 0);
num_digits += 1;
} else { // Digit without zero
decimal_point = 0;
}
} else { // Both parts. Trailing zeros are part of significands.
integral_size = output_exp + 1;
num_digits += 1; // Decimal point.
if (specs.alternate_form && fp_specs.format == fp_format::general) { // ({:#g}, 1.2) -> 1.20000 instead 1.2
num_zeros = fp_specs.precision - significand_size;
}
if (fp_specs.format == fp_format::fixed && significand_size > integral_size &&
significand_size - integral_size < fp_specs.precision) { // ({:.4}, 0.99999) -> 1.0000 instead of 1.00
num_zeros = fp_specs.precision - (significand_size - integral_size);
}
}
if (num_zeros < 0) {
num_zeros = 0;
}
if (num_zeros_2 < 0) {
num_zeros_2 = 0;
}
num_digits += static_cast<size_t>(num_zeros + num_zeros_2);
total_width += num_digits;
return write_padded<alignment::right>(out, specs, total_width, [&]() noexcept -> result<void> {
const size_t area_size = num_digits + static_cast<size_t>(sign_to_write != no_sign);
EMIO_TRY(auto area, out.get_buffer().get_write_area_of(area_size));
auto* it = area.data();
if (sign_to_write != no_sign) {
*it++ = sign_to_write;
}
if (output_exp < 0) {
*it++ = '0';
if (decimal_point != 0) {
*it++ = decimal_point;
it = fill_n(it, num_zeros, '0'); // TODO: simplify fill_n/copy/copy/n + it
it = copy_n(significand, significand_size, it);
fill_n(it, num_zeros_2, '0');
}
} else if ((output_exp + 1) >= significand_size) {
it = copy_n(significand, integral_size, it);
if (num_zeros != 0) {
it = fill_n(it, num_zeros, '0');
}
if (decimal_point != 0) {
*it++ = '.';
if (num_zeros_2 != 0) {
fill_n(it, num_zeros_2, '0');
}
}
} else {
it = write_significand(it, significand, significand_size, integral_size, decimal_point);
if (num_zeros != 0) {
fill_n(it, num_zeros, '0');
}
}
return success;
});
}
inline constexpr std::array<char, 1> zero_digit{'0'};
inline constexpr format_fp_result_t format_decimal(buffer& buffer, const fp_format_specs& fp_specs,
const decode_result_t& decoded) noexcept {
if (decoded.category == category::zero) {
return format_fp_result_t{zero_digit, 1};
}
switch (fp_specs.format) {
case fp_format::general:
if (fp_specs.precision == no_precision) {
return format_shortest(decoded.finite, buffer);
}
[[fallthrough]];
case fp_format::exp:
return format_exact(decoded.finite, buffer, format_exact_mode::significand_digits, fp_specs.precision);
case fp_format::fixed: {
auto res = format_exact(decoded.finite, buffer, format_exact_mode::decimal_point, fp_specs.precision);
if (res.digits.empty()) {
return format_fp_result_t{zero_digit, 1};
}
return res;
}
case fp_format::hex:
std::terminate();
}
EMIO_Z_INTERNAL_UNREACHABLE;
}
inline constexpr result<void> format_and_write_decimal(writer& out, format_specs& specs,
const decode_result_t& decoded) noexcept {
fp_format_specs fp_specs = parse_fp_format_specs(specs);
if (decoded.category == category::infinity || decoded.category == category::nan) {
if (specs.zero_flag) { // Words aren't prefixed with zeros.
specs.fill = ' ';
specs.zero_flag = false;
}
EMIO_TRY(const char sign_to_write, try_write_sign(out, specs, decoded.negative));
const size_t total_length = 3 + static_cast<uint32_t>(sign_to_write != no_sign);
return write_padded<alignment::left>(out, specs, total_length, [&]() noexcept -> result<void> {
if (sign_to_write != no_sign) {
EMIO_TRYV(out.write_char(sign_to_write));
}
return write_non_finite(out, fp_specs.upper_case, decoded.category == category::infinity);
});
}
emio::memory_buffer buf;
const format_fp_result_t res = format_decimal(buf, fp_specs, decoded);
return write_decimal(out, specs, fp_specs, decoded.negative, res);
}
template <typename Arg>
requires(std::is_floating_point_v<Arg> && sizeof(Arg) <= sizeof(double))
constexpr result<void> write_arg(writer& out, format_specs& specs, const Arg& arg) noexcept {
return format_and_write_decimal(out, specs, decode(arg));
}
inline constexpr result<void> write_arg(writer& out, format_specs& specs, std::string_view arg) noexcept {
if (specs.type != '?') {
if (specs.precision >= 0) {
arg = unchecked_substr(arg, 0, static_cast<size_t>(specs.precision));
}
return write_padded<alignment::left>(out, specs, arg.size(), [&]() noexcept {
return out.write_str(arg);
});
}
const size_t escaped_size = detail::count_size_when_escaped(arg);
return write_padded<alignment::left>(out, specs, escaped_size + 2U /* quotes */, [&]() noexcept {
return detail::write_str_escaped(out.get_buffer(), arg, escaped_size, '"');
});
}
template <typename Arg>
requires(std::is_same_v<Arg, char>)
constexpr result<void> write_arg(writer& out, format_specs& specs, const Arg arg) noexcept {
// If a type other than None/c is specified, write out as integer instead of char.
if (specs.type != no_type && specs.type != 'c' && specs.type != '?') {
return write_arg(out, specs, static_cast<uint8_t>(arg));
}
if (specs.type != '?') {
return write_padded<alignment::left>(out, specs, 1, [&]() noexcept {
return out.write_char(arg);
});
}
return write_padded<alignment::left>(out, specs, 3, [&]() noexcept {
return out.write_char_escaped(arg);
});
}
template <typename T>
inline constexpr bool is_void_pointer_v =
std::is_pointer_v<T> && std::is_same_v<std::remove_cv_t<std::remove_pointer_t<T>>, void>;
template <typename Arg>
requires(is_void_pointer_v<Arg> || std::is_null_pointer_v<Arg>)
constexpr result<void> write_arg(writer& out, format_specs& specs, Arg arg) noexcept {
specs.alternate_form = true;
specs.type = 'x';
if constexpr (std::is_null_pointer_v<Arg>) {
return write_arg(out, specs, uintptr_t{0});
} else {
return write_arg(out, specs, std::bit_cast<uintptr_t>(arg));
}
}
template <typename Arg>
requires(std::is_same_v<Arg, bool>)
constexpr result<void> write_arg(writer& out, format_specs& specs, Arg arg) noexcept {
// If a type other than None/s is specified, write out as 1/0 instead of true/false.
if (specs.type != no_type && specs.type != 's') {
return write_arg(out, specs, static_cast<uint8_t>(arg));
}
if (arg) {
return write_padded<alignment::left>(out, specs, 4, [&]() noexcept {
return out.write_str("true"sv);
});
}
return write_padded<alignment::left>(out, specs, 5, [&]() noexcept {
return out.write_str("false"sv);
});
}
//
// Checks.
//
// specs is passed by reference instead as return type to reduce copying of big value (and code bloat)
inline constexpr result<void> validate_format_specs(reader& format_rdr, format_specs& specs) noexcept {
EMIO_TRY(char c, format_rdr.read_char());
if (c == '}') { // Format end.
return success;
}
if (c == '{') { // No dynamic spec support.
return err::invalid_format;
}
{
// Parse for alignment specifier.
EMIO_TRY(const char c2, format_rdr.peek());
if (c2 == '<' || c2 == '^' || c2 == '>') {
if (c2 == '<') {
specs.align = alignment::left;
} else if (c2 == '^') {
specs.align = alignment::center;
} else {
specs.align = alignment::right;
}
specs.fill = c;
format_rdr.pop();
EMIO_TRY(c, format_rdr.read_char());
} else if (c == '<' || c == '^' || c == '>') {
if (c == '<') {
specs.align = alignment::left;
} else if (c == '^') {
specs.align = alignment::center;
} else {
specs.align = alignment::right;
}
EMIO_TRY(c, format_rdr.read_char());
}
}
if (c == '+' || c == '-' || c == ' ') { // Sign.
specs.sign = c;
EMIO_TRY(c, format_rdr.read_char());
}
if (c == '#') { // Alternate form.
specs.alternate_form = true;
EMIO_TRY(c, format_rdr.read_char());
}
if (c == '0') { // Zero flag.
if (specs.align == alignment::none) { // If fill/align is used, the zero flag is ignored.
specs.fill = '0';
specs.align = alignment::right;
specs.zero_flag = true;
}
EMIO_TRY(c, format_rdr.read_char());
}
if (detail::isdigit(c)) { // Width.
format_rdr.unpop();
EMIO_TRY(const uint32_t width, format_rdr.parse_int<uint32_t>());
if (width > (static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))) {
return err::invalid_format;
}
specs.width = static_cast<int32_t>(width);
EMIO_TRY(c, format_rdr.read_char());
}
if (c == '.') { // Precision.
if (const result<char> next = format_rdr.peek();
next && !isdigit(next.assume_value())) { // Not followed by a digit.
return err::invalid_format;
}
EMIO_TRY(const uint32_t precision, format_rdr.parse_int<uint32_t>());
if (precision > (static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))) {
return err::invalid_format;
}
specs.precision = static_cast<int32_t>(precision);
EMIO_TRY(c, format_rdr.read_char());
}
if (detail::isalpha(c) || c == '?') { // Type.
specs.type = c;
EMIO_TRY(c, format_rdr.read_char());
}
if (c == '}') { // Format end.
return success;
}
return err::invalid_format;
}
inline constexpr result<void> parse_format_specs(reader& format_rdr, format_specs& specs) noexcept {
char c = format_rdr.read_char().assume_value();
if (c == '}') { // Format end.
return success;
}
{
// Parse for alignment specifier.
const char c2 = format_rdr.peek().assume_value();
if (c2 == '<' || c2 == '^' || c2 == '>') {
if (c2 == '<') {
specs.align = alignment::left;
} else if (c2 == '^') {
specs.align = alignment::center;
} else {
specs.align = alignment::right;
}
specs.fill = c;
format_rdr.pop();
c = format_rdr.read_char().assume_value();
} else if (c == '<' || c == '^' || c == '>') {
if (c == '<') {
specs.align = alignment::left;
} else if (c == '^') {
specs.align = alignment::center;
} else {
specs.align = alignment::right;
}
c = format_rdr.read_char().assume_value();
}
}
if (c == '+' || c == '-' || c == ' ') { // Sign.
specs.sign = c;
c = format_rdr.read_char().assume_value();
}
if (c == '#') { // Alternate form.
specs.alternate_form = true;
c = format_rdr.read_char().assume_value();
}
if (c == '0') { // Zero flag.
if (specs.align == alignment::none) { // Ignoreable.
specs.fill = '0';
specs.align = alignment::right;
specs.zero_flag = true;
}
c = format_rdr.read_char().assume_value();
}
if (detail::isdigit(c)) { // Width.
format_rdr.unpop();
specs.width = static_cast<int32_t>(format_rdr.parse_int<uint32_t>().assume_value());
c = format_rdr.read_char().assume_value();
}
if (c == '.') { // Precision.
specs.precision = static_cast<int32_t>(format_rdr.parse_int<uint32_t>().assume_value());
c = format_rdr.read_char().assume_value();
}
if (detail::isalpha(c) || c == '?') { // Type.
specs.type = c;
format_rdr.pop(); // format_rdr.read_char() in validate_format_specs;
}
return success;
}
inline constexpr result<void> check_integral_specs(const format_specs& specs) noexcept {
if (specs.precision != no_precision) {
return err::invalid_format;
}
switch (specs.type) {
case no_type:
case 'd':
case 'x':
case 'X':
case 'b':
case 'B':
case 'c':
case 'o':
case 'O':
return success;
default:
return err::invalid_format;
}
}
inline constexpr result<void> check_unsigned_specs(const format_specs& specs) noexcept {
if (specs.sign == no_sign) {
return success;
}
return err::invalid_format;
}
inline constexpr result<void> check_bool_specs(const format_specs& specs) noexcept {
if (specs.type != no_type && specs.type != 's') {
return check_integral_specs(specs);
}
if (specs.precision != no_precision) {
return err::invalid_format;
}
return success;
}
inline constexpr result<void> check_char_specs(const format_specs& specs) noexcept {
if (specs.type != no_type && specs.type != 'c' && specs.type != '?') {
return check_integral_specs(specs);
}
if (specs.alternate_form || specs.sign != no_sign || specs.zero_flag || specs.precision != no_precision) {
return err::invalid_format;
}
return success;
}
inline constexpr result<void> check_pointer_specs(const format_specs& specs) noexcept {
if (specs.type != no_type && specs.type != 'p') {
return err::invalid_format;
}
if (specs.alternate_form || specs.sign != no_sign || specs.zero_flag || specs.precision != no_precision) {
return err::invalid_format;
}
return success;
}
inline constexpr result<void> check_floating_point_specs(const format_specs& specs) noexcept {
if (specs.precision > 1100) {
return err::invalid_format;
}
switch (specs.type) {
case no_type:
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
// case 'a': Not supported yet.
// case 'A':
return success;
default:
return err::invalid_format;
}
}
inline constexpr result<void> check_string_specs(const format_specs& specs) noexcept {
if (specs.alternate_form || specs.sign != no_sign || specs.zero_flag ||
(specs.precision != no_precision && specs.type == '?') ||
(specs.type != no_type && specs.type != 's' && specs.type != '?')) {
return err::invalid_format;
}
return success;
}
//
// Type traits.
//
// Specifies if T has an enabled formatter specialization.
template <typename Arg>
inline constexpr bool has_formatter_v = std::is_constructible_v<formatter<Arg>>;
template <typename Arg>
inline constexpr bool format_can_fail_v = false;
template <typename Arg>
requires requires(Arg) { formatter<Arg>::format_can_fail; }
inline constexpr bool format_can_fail_v<Arg> = formatter<Arg>::format_can_fail;
template <typename T>
concept has_validate_function_v = requires {
{ formatter<T>::validate(std::declval<reader&>()) } -> std::same_as<result<void>>;
};
template <typename T>
concept has_static_validate_function_v = requires { &formatter<T>::validate; };
template <typename T>
concept has_member_validate_function_v = requires { std::declval<formatter<T>>().validate(std::declval<reader&>()); };
template <typename T>
concept has_any_validate_function_v =
has_static_validate_function_v<T> || std::is_member_function_pointer_v<decltype(&formatter<T>::validate)> ||
has_member_validate_function_v<T>;
template <typename Arg>
constexpr result<void> validate_trait(reader& format_rdr) {
// Check if a formatter exist and a correct validate method is implemented. If not, use the parse method.
if constexpr (has_formatter_v<Arg>) {
if constexpr (has_validate_function_v<Arg>) {
return formatter<Arg>::validate(format_rdr);
} else {
static_assert(!has_any_validate_function_v<Arg>,
"Formatter seems to have a validate property which doesn't fit the desired signature.");
return formatter<Arg>{}.parse(format_rdr);
}
} else {
static_assert(has_formatter_v<Arg>,
"Cannot format an argument. To make type T formattable provide a formatter<T> specialization.");
return err::invalid_format;
}
}
template <typename T>
inline constexpr bool is_core_type_v =
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> ||
std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t> || std::is_same_v<T, double> ||
std::is_null_pointer_v<T> || is_void_pointer_v<T> || std::is_same_v<T, std::string_view>;
template <typename T>
concept has_format_as = requires(T arg) { format_as(arg); };
template <typename T>
using format_as_return_t = decltype(format_as(std::declval<T>()));
// To reduce code bloat, similar types are unified to a general one.
template <typename T>
struct unified_type;
template <typename T>
struct unified_type {
using type = const T&;
};
template <typename T>
requires(!std::is_integral_v<T> && !std::is_null_pointer_v<T> && std::is_convertible_v<T, std::string_view>)
struct unified_type<T> {
using type = std::string_view;
};
template <typename T>
requires(std::is_integral_v<T> && std::is_signed_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<T, char>)
struct unified_type<T> {
using type = std::conditional_t<num_bits<T>() <= 32, int32_t, int64_t>;
};
template <typename T>
requires(std::is_floating_point_v<T> && sizeof(T) <= sizeof(double))
struct unified_type<T> {
using type = double;
};
template <typename T>
requires(std::is_same_v<T, char> || std::is_same_v<T, bool> || is_void_pointer_v<T> || std::is_null_pointer_v<T>)
struct unified_type<T> {
using type = T;
};
template <typename T>
requires(std::is_integral_v<T> && std::is_unsigned_v<T> && !std::is_same_v<T, bool> && !std::is_same_v<T, char>)
struct unified_type<T> {
using type = std::conditional_t<num_bits<T>() <= 32, uint32_t, uint64_t>;
};
template <typename T>
using unified_type_t = typename unified_type<T>::type;
} // namespace detail::format
} // namespace emio
================================================
FILE: include/emio/detail/format/parser.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include "args.hpp"
#include "formatter.hpp"
namespace emio::detail::format {
class format_parser final : public parser<format_parser, input_validation::disabled> {
public:
constexpr explicit format_parser(writer& out, reader& format_rdr) noexcept
: parser<format_parser, input_validation::disabled>{format_rdr}, out_{out} {}
format_parser(const format_parser&) = delete;
format_parser(format_parser&&) = delete;
format_parser& operator=(const format_parser&) = delete;
format_parser& operator=(format_parser&&) = delete;
constexpr ~format_parser() noexcept override; // NOLINT(performance-trivially-destructible): See definition.
constexpr result<void> process(const std::string_view& str) noexcept override {
return out_.write_str(str);
}
result<void> process_arg(const format_arg& arg) noexcept {
return arg.process_arg(out_, format_rdr_);
}
template <typename Arg>
constexpr result<void> process_arg(const Arg& arg) noexcept {
if constexpr (has_formatter_v<Arg>) {
formatter<Arg> formatter;
EMIO_TRYV(formatter.parse(this->format_rdr_));
return formatter.format(out_, arg);
} else {
static_assert(has_formatter_v<Arg>,
"Cannot format an argument. To make type T formattable provide a formatter<T> specialization.");
}
}
private:
writer& out_;
};
// Explicit out-of-class definition because of GCC bug: <destructor> used before its definition.
constexpr format_parser::~format_parser() noexcept = default;
class format_specs_checker final : public parser<format_specs_checker, input_validation::enabled> {
public:
using parser<format_specs_checker, input_validation::enabled>::parser;
format_specs_checker(const format_specs_checker& other) = delete;
format_specs_checker(format_specs_checker&& other) = delete;
format_specs_checker& operator=(const format_specs_checker& other) = delete;
format_specs_checker& operator=(format_specs_checker&& other) = delete;
constexpr ~format_specs_checker() noexcept override; // NOLINT(performance-trivially-destructible): See definition.
result<void> process_arg(const format_validation_arg& arg) noexcept {
return arg.validate(this->format_rdr_);
}
template <typename Arg>
constexpr result<void> process_arg(std::type_identity<Arg> /*unused*/) noexcept {
return format_arg_trait<std::remove_cvref_t<Arg>>::validate(this->format_rdr_);
}
};
// Explicit out-of-class definition because of GCC bug: <destructor> used before its definition.
constexpr format_specs_checker::~format_specs_checker() noexcept = default;
} // namespace emio::detail::format
================================================
FILE: include/emio/detail/format/ranges.hpp
================================================
//
// Copyright (c) 2023 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include <span>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../../formatter.hpp"
namespace emio::detail::format {
// For range.
using std::begin;
using std::data;
using std::end;
using std::size;
template <typename T>
concept advanceable = requires(T x) { ++x; };
template <typename T>
concept is_iterable = std::is_array_v<T> || requires(T x) {
{ begin(x) } -> advanceable;
requires !std::is_same_v<decltype(*begin(x)), void>;
{ static_cast<bool>(begin(x) != end(x)) };
};
template <typename T>
using element_type_t = std::remove_cvref_t<decltype(*begin(std::declval<std::remove_reference_t<T>&>()))>;
template <typename T>
concept is_map = requires(T x) { typename T::mapped_type; };
template <typename T>
concept is_set = requires(T x) { typename T::key_type; };
template <typename T>
concept is_string_like = !std::is_null_pointer_v<T> && std::is_convertible_v<T, std::string_view>;
template <typename T>
concept is_valid_range = is_iterable<T> && !is_string_like<T> && is_formattable_v<element_type_t<T>>;
template <typename T>
struct is_span : std::false_type {};
template <typename T, size_t N>
struct is_span<std::span<T, N>> : std::true_type {};
template <typename T>
concept is_contiguous_but_not_span = std::is_array_v<T> || requires(T x) {
requires !is_span<T>::value;
requires std::is_same_v<std::remove_cvref_t<decltype(*data(x))>, element_type_t<T>>;
{ size(x) } -> std::same_as<size_t>;
};
struct ranges_specs {
std::string_view opening_bracket{};
std::string_view closing_bracket{};
std::string_view separator{};
};
template <typename Formatter>
requires requires(Formatter f) { f.set_debug_format(true); }
constexpr void maybe_set_debug_format(Formatter& f, bool set) noexcept {
f.set_debug_format(set);
}
template <typename Formatter>
constexpr void maybe_set_debug_format(Formatter& /*unused*/, ...) noexcept {}
// For tuple like types.
using std::get;
// From https://stackoverflow.com/a/68444475/1611317
template <class T, std::size_t N>
concept has_tuple_element = requires(T t) {
typename std::tuple_element_t<N, std::remove_const_t<T>>;
{ get<N>(t) } -> std::convertible_to<const std::tuple_element_t<N, T>&>;
};
template <typename T, size_t... Ns>
constexpr auto has_tuple_element_unpack(std::index_sequence<Ns...> /*unused*/) noexcept {
return (has_tuple_element<T, Ns> && ...);
}
template <class T>
concept is_tuple_like = !std::is_reference_v<T> && requires(T t) {
typename std::tuple_size<T>::type;
requires std::derived_from<std::tuple_size<T>, std::integral_constant<std::size_t, std::tuple_size_v<T>>>;
} && has_tuple_element_unpack<T>(std::make_index_sequence<std::tuple_size_v<T>>());
template <typename T, size_t... Ns>
constexpr auto is_formattable_unpack(std::index_sequence<Ns...> /*unused*/) noexcept {
return (is_formattable_v<decltype(get<Ns>(std::declval<T&>()))> && ...);
}
template <typename T>
concept is_valid_tuple = !is_valid_range<T> && is_tuple_like<T> &&
is_formattable_unpack<T>(std::make_index_sequence<std::tuple_size_v<T>>());
template <typename T, std::size_t... Ns>
auto get_tuple_formatters(std::index_sequence<Ns...> /*unused*/)
-> std::tuple<formatter<std::remove_cvref_t<std::tuple_element_t<Ns, T>>>...>;
template <typename T>
using tuple_formatters = decltype(get_tuple_formatters<T>(std::make_index_sequence<std::tuple_size_v<T>>{}));
} // namespace emio::detail::format
================================================
FILE: include/emio/detail/format/specs.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include <cstdint>
namespace emio::detail::format {
// "{" [arg_id] [":" (format_spec)]
// replacement_field ::= "{" [arg_id] [":" (format_spec)] "}"
// arg_id ::= integer
// integer ::= digit+
// digit ::= "0"..."9"
// format_spec ::= [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
// fill ::= <a character other than '{' or '}'>
// align ::= "<" | ">" | "^"
// sign ::= "+" | "-" | " "
// width ::= integer (<=int max)
// precision ::= integer (<=int max)
// type ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G"| "o" | "O" | "p" | "s" | "x"
// | "X"
inline constexpr char no_sign = '\0';
inline constexpr int no_precision = -1;
inline constexpr char no_type = 0;
enum class alignment : uint8_t { none, left, center, right };
struct format_specs {
char fill{' '};
alignment align{alignment::none};
char sign{no_sign};
bool alternate_form{false};
bool zero_flag{false};
int32_t width{0};
int32_t precision{no_precision};
char type{no_type};
};
} // namespace emio::detail::format
================================================
FILE: include/emio/detail/misc.hpp
================================================
//
// Copyright (c) 20213- present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include <type_traits>
namespace emio::detail {
template <typename T>
struct always_false : std::false_type {};
template <typename T>
inline constexpr bool always_false_v = always_false<T>::value;
template <typename... Ts>
struct overload : Ts... {
using Ts::operator()...;
};
template <class... Ts>
overload(Ts...) -> overload<Ts...>;
} // namespace emio::detail
================================================
FILE: include/emio/detail/parser.hpp
================================================
//
// Copyright (c) 2021 - present, Toni Neubert
// All rights reserved.
//
// For the license information refer to emio.hpp
#pragma once
#include <cstdint>
#include <limits>
#include <optional>
#include <string_view>
#include "../reader.hpp"
#include "args.hpp"
#include "bitset.hpp"
namespace emio::detail {
/**
* Flag to enable/disable input validation.
*/
enum class input_validation { enabled, disabled };
inline constexpr uint8_t no_more_args = std::numeric_limits<uint8_t>::max();
template <input_validation>
class parser_base {
public:
constexpr explicit parser_base(reader& format_rdr) noexcept : format_rdr_{format_rdr} {}
parser_base(const parser_base&) = delete;
parser_base(parser_base&&) = delete;
parser_base& operator=(const parser_base&) = delete;
parser_base& operator=(parser_base&&) = delete;
virtual constexpr ~parser_base() = default;
constexpr result<void> parse(uint8_t& arg_nbr) noexcept {
const char*& it = get_it(format_rdr_);
const char* const end = get_end(format_rdr_);
while (it != end) {
const char c = *it++;
if (c == '{') {
if (it == end) {
return emio::err::invalid_format;
}
if (*it == '{') {
++it;
} else {
return parse_replacement_field(arg_nbr);
}
} else if (c == '}') {
if (it == end || *it != '}') {
return err::invalid_format;
}
++it;
}
}
return success;
}
protected:
reader& format_rdr_;
private:
constexpr result<void> parse_replacement_field(uint8_t& arg_nbr) noexcept {
EMIO_TRYV(parse_field_name(arg_nbr));
EMIO_TRY(const char c, format_rdr_.peek());
if (c == '}') {
return success;
}
if (c == ':') { // Format specs.
// Format specs are parsed with known argument type later.
format_rdr_.pop();
return success;
}
return err::invalid_format;
}
constexpr result<void> parse_field_name(uint8_t& arg_nbr) noexcept {
EMIO_TRY(const char c, format_rdr_.peek());
if (detail::isdigit(c)) { // Positional argument.
if (use_positional_args_ == false) { // If first argument was positional -> failure.
return err::invalid_format;
}
EMIO_TRY(arg_nbr, format_rdr_.template parse_int
gitextract_uxa4xgnn/
├── .clang-format
├── .clang-tidy
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── pages.yml
├── .gitignore
├── CMakeLists.txt
├── CMakePresets.json
├── LICENSE
├── README.md
├── cmake/
│ ├── arm-none-eabi-toolchain.cmake
│ ├── coverage.cmake
│ ├── dev-mode.cmake
│ ├── elf_to_size_coverage.py
│ ├── folders.cmake
│ ├── install-config.cmake
│ ├── install-rules.cmake
│ ├── lint-targets.cmake
│ ├── lint.cmake
│ ├── prelude.cmake
│ ├── project-is-top-level.cmake
│ ├── size-coverage.cmake
│ └── variables.cmake
├── docs/
│ ├── API.md
│ ├── CODE_OF_CONDUCT.md
│ ├── CONTRIBUTING.md
│ ├── DESIGN.md
│ ├── DEVELOPING.md
│ └── res/
│ └── class_diagram.puml
├── include/
│ └── emio/
│ ├── buffer.hpp
│ ├── detail/
│ │ ├── args.hpp
│ │ ├── bignum.hpp
│ │ ├── bitset.hpp
│ │ ├── conversion.hpp
│ │ ├── ct_vector.hpp
│ │ ├── format/
│ │ │ ├── args.hpp
│ │ │ ├── decode.hpp
│ │ │ ├── dragon.hpp
│ │ │ ├── format_to.hpp
│ │ │ ├── formatter.hpp
│ │ │ ├── parser.hpp
│ │ │ ├── ranges.hpp
│ │ │ └── specs.hpp
│ │ ├── misc.hpp
│ │ ├── parser.hpp
│ │ ├── predef.hpp
│ │ ├── scan/
│ │ │ ├── args.hpp
│ │ │ ├── parser.hpp
│ │ │ ├── scan_from.hpp
│ │ │ ├── scanner.hpp
│ │ │ └── specs.hpp
│ │ ├── utf.hpp
│ │ ├── validated_string.hpp
│ │ └── validated_string_storage.hpp
│ ├── emio.hpp
│ ├── format.hpp
│ ├── formatter.hpp
│ ├── iterator.hpp
│ ├── ranges.hpp
│ ├── reader.hpp
│ ├── result.hpp
│ ├── scan.hpp
│ ├── scanner.hpp
│ ├── std.hpp
│ └── writer.hpp
└── test/
├── benchmark/
│ ├── CMakeLists.txt
│ ├── bench_format.cpp
│ └── bench_scan.cpp
├── compile_test/
│ ├── CMakeLists.txt
│ └── compile.cpp
├── fuzzy/
│ └── dragon4/
│ ├── .gitignore
│ ├── Makefile
│ ├── main.cpp
│ ├── rust_ref/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ └── seeds/
│ └── test1
├── size_test/
│ ├── CMakeLists.txt
│ ├── base.cpp
│ ├── emio/
│ │ ├── doFormat_a.cpp
│ │ ├── format_all.cpp
│ │ ├── format_all_and_extra.cpp
│ │ ├── format_and_scan_all.cpp
│ │ ├── format_and_scan_all_runtime.cpp
│ │ ├── format_and_write_int.cpp
│ │ ├── format_double.cpp
│ │ ├── format_int.cpp
│ │ ├── format_int_twice.cpp
│ │ ├── format_runtime.cpp
│ │ ├── format_to.cpp
│ │ ├── format_to_n.cpp
│ │ ├── scan_all.cpp
│ │ ├── scan_int.cpp
│ │ ├── vformat.cpp
│ │ └── write_int.cpp
│ ├── fmt/
│ │ ├── doFormat_a.cpp
│ │ ├── fmt_dragon.cpp
│ │ ├── fmt_grisu.cpp
│ │ ├── fmt_grisu_and_dragon.cpp
│ │ ├── format_all.cpp
│ │ ├── format_all_and_extra.cpp
│ │ ├── format_int.cpp
│ │ ├── format_int_twice.cpp
│ │ ├── format_runtime.cpp
│ │ ├── format_to.cpp
│ │ ├── format_to_n.cpp
│ │ └── vformat.cpp
│ ├── std/
│ │ ├── locale.cpp
│ │ ├── snprintf.cpp
│ │ ├── snprintf_and_sscanf.cpp
│ │ ├── sscanf.cpp
│ │ ├── string.cpp
│ │ ├── string_stream.cpp
│ │ ├── to_chars.cpp
│ │ ├── to_string_double.cpp
│ │ └── to_string_int.cpp
│ └── stubs.cpp
├── static_analysis/
│ ├── CMakeLists.txt
│ └── test_main.cpp
└── unit_test/
├── CMakeLists.txt
├── detail/
│ ├── test_bignum.cpp
│ ├── test_bitset.cpp
│ ├── test_conversion.cpp
│ ├── test_ct_vector.cpp
│ ├── test_decode.cpp
│ ├── test_dragon.cpp
│ └── test_utf.cpp
├── integer_ranges.hpp
├── test_buffer.cpp
├── test_dynamic_format_spec.cpp
├── test_format.cpp
├── test_format_api.cpp
├── test_format_as.cpp
├── test_format_could_fail_api.cpp
├── test_format_emio_vs_fmt.cpp
├── test_format_string.cpp
├── test_format_to_api.cpp
├── test_format_to_n_api.cpp
├── test_formatted_size.cpp
├── test_formatter.cpp
├── test_iterator.cpp
├── test_print.cpp
├── test_ranges.cpp
├── test_reader.cpp
├── test_result.cpp
├── test_scan.cpp
├── test_std.cpp
└── test_writer.cpp
SYMBOL INDEX (869 symbols across 92 files)
FILE: cmake/elf_to_size_coverage.py
function parse_args (line 11) | def parse_args(args):
class Generator (line 23) | class Generator:
method __init__ (line 24) | def __init__(self, size_tool, base_file, out):
method generate (line 33) | def generate(self, file: Path):
method _get_size (line 50) | def _get_size(self, file: Path):
function run (line 61) | def run():
FILE: include/emio/buffer.hpp
type emio (line 24) | namespace emio {
class buffer (line 33) | class buffer {
method buffer (line 35) | constexpr buffer(const buffer& other) = delete;
method buffer (line 36) | constexpr buffer(buffer&& other) = delete;
method buffer (line 37) | constexpr buffer& operator=(const buffer& other) = delete;
method buffer (line 38) | constexpr buffer& operator=(buffer&& other) = delete;
method get_write_area_of (line 46) | constexpr result<std::span<char>> get_write_area_of(size_t size) noe...
method get_write_area_of_max (line 62) | constexpr result<std::span<char>> get_write_area_of_max(size_t size)...
type fixed_size (line 85) | enum class fixed_size : bool { no, yes }
method buffer (line 91) | constexpr explicit buffer(fixed_size fixed = fixed_size::no) noexcep...
method request_write_area (line 99) | virtual constexpr result<std::span<char>> request_write_area(const s...
method set_write_area (line 109) | constexpr void set_write_area(const std::span<char> area) noexcept {
method get_used_count (line 118) | [[nodiscard]] constexpr size_t get_used_count() const noexcept {
class memory_buffer (line 133) | class memory_buffer final : public buffer {
method memory_buffer (line 138) | constexpr memory_buffer() noexcept : memory_buffer{0} {}
method memory_buffer (line 144) | constexpr explicit memory_buffer(const size_t capacity) noexcept {
method memory_buffer (line 149) | constexpr memory_buffer(const memory_buffer& other)
method memory_buffer (line 154) | constexpr memory_buffer(memory_buffer&& other) noexcept
method memory_buffer (line 160) | constexpr memory_buffer& operator=(const memory_buffer& other) {
method memory_buffer (line 171) | constexpr memory_buffer& operator=(memory_buffer&& other) noexcept {
method view (line 189) | [[nodiscard]] constexpr std::string_view view() const noexcept {
method str (line 198) | [[nodiscard]] std::string str() const {
method reset (line 206) | constexpr void reset() noexcept {
method capacity (line 216) | [[nodiscard]] constexpr size_t capacity() const noexcept {
method request_write_area (line 221) | constexpr result<std::span<char>> request_write_area(const size_t us...
class span_buffer (line 238) | class span_buffer : public buffer {
method span_buffer (line 243) | constexpr span_buffer() : buffer{fixed_size::yes} {}
method span_buffer (line 249) | constexpr explicit span_buffer(const std::span<char> span) noexcept ...
method span_buffer (line 253) | constexpr span_buffer(const span_buffer& other) : buffer{fixed_size:...
method span_buffer (line 259) | constexpr span_buffer(span_buffer&& other) noexcept : span_buffer{st...
method span_buffer (line 261) | constexpr span_buffer& operator=(const span_buffer& other) {
method span_buffer (line 272) | constexpr span_buffer& operator=(span_buffer&& other) noexcept {
method view (line 283) | [[nodiscard]] constexpr std::string_view view() const noexcept {
method str (line 292) | [[nodiscard]] std::string str() const {
method reset (line 300) | constexpr void reset() noexcept {
method capacity (line 308) | [[nodiscard]] constexpr size_t capacity() const noexcept {
class static_buffer (line 324) | class static_buffer final : private std::array<char, StorageSize>, pub...
method static_buffer (line 329) | constexpr static_buffer() noexcept : span_buffer{std::span{*this}} {}
method static_buffer (line 331) | constexpr static_buffer(const static_buffer& other) : static_buffer() {
method static_buffer (line 337) | constexpr static_buffer(static_buffer&& other) noexcept : static_buf...
method static_buffer (line 339) | constexpr static_buffer& operator=(const static_buffer& other) {
method static_buffer (line 350) | constexpr static_buffer& operator=(static_buffer&& other) noexcept {
type detail (line 361) | namespace detail {
function Container (line 365) | Container& get_container(std::back_insert_iterator<Container> it) no...
type get_value_type (line 376) | struct get_value_type {
type get_value_type<std::back_insert_iterator<Container>> (line 381) | struct get_value_type<std::back_insert_iterator<Container>> {
type get_value_type<std::ostreambuf_iterator<Char, Traits>> (line 387) | struct get_value_type<std::ostreambuf_iterator<Char, Traits>> {
function copy_str (line 396) | constexpr auto copy_str(InputIt it, InputIt end, OutputIt out) -> Ou...
class counting_buffer (line 709) | class counting_buffer final : public buffer {
method counting_buffer (line 712) | constexpr counting_buffer() noexcept = default;
method counting_buffer (line 713) | constexpr counting_buffer(const counting_buffer&) = delete;
method counting_buffer (line 714) | constexpr counting_buffer(counting_buffer&&) noexcept = delete;
method counting_buffer (line 715) | constexpr counting_buffer& operator=(const counting_buffer&) = del...
method counting_buffer (line 716) | constexpr counting_buffer& operator=(counting_buffer&&) noexcept =...
method count (line 723) | [[nodiscard]] constexpr size_t count() const noexcept {
method request_write_area (line 728) | constexpr result<std::span<char>> request_write_area(const size_t ...
class iterator_buffer (line 409) | class iterator_buffer
class iterator_buffer<Iterator, CacheSize> (line 419) | class iterator_buffer<Iterator, CacheSize> final : public buffer {
method iterator_buffer (line 426) | constexpr explicit iterator_buffer(Iterator it) noexcept : it_{it} {
method iterator_buffer (line 430) | iterator_buffer(const iterator_buffer&) = delete;
method iterator_buffer (line 431) | iterator_buffer(iterator_buffer&&) = delete;
method iterator_buffer (line 432) | iterator_buffer& operator=(const iterator_buffer&) = delete;
method iterator_buffer (line 433) | iterator_buffer& operator=(iterator_buffer&&) = delete;
method flush (line 440) | constexpr result<void> flush() noexcept {
method Iterator (line 450) | constexpr Iterator out() noexcept {
method request_write_area (line 456) | constexpr result<std::span<char>> request_write_area(const size_t /*...
class iterator_buffer<OutputPtr*> (line 478) | class iterator_buffer<OutputPtr*> final : public buffer {
method iterator_buffer (line 484) | constexpr explicit iterator_buffer(OutputPtr* ptr) noexcept : ptr_{p...
method iterator_buffer (line 488) | iterator_buffer(const iterator_buffer&) = delete;
method iterator_buffer (line 489) | iterator_buffer(iterator_buffer&&) = delete;
method iterator_buffer (line 490) | iterator_buffer& operator=(const iterator_buffer&) = delete;
method iterator_buffer (line 491) | iterator_buffer& operator=(iterator_buffer&&) = delete;
method flush (line 498) | constexpr result<void> flush() noexcept {
method OutputPtr (line 507) | constexpr OutputPtr* out() noexcept {
class iterator_buffer<std::back_insert_iterator<Container>, Capacity> (line 522) | class iterator_buffer<std::back_insert_iterator<Container>, Capacity> ...
method iterator_buffer (line 528) | constexpr explicit iterator_buffer(std::back_insert_iterator<Contain...
method iterator_buffer (line 533) | iterator_buffer(const iterator_buffer&) = delete;
method iterator_buffer (line 534) | iterator_buffer(iterator_buffer&&) = delete;
method iterator_buffer (line 535) | iterator_buffer& operator=(const iterator_buffer&) = delete;
method iterator_buffer (line 536) | iterator_buffer& operator=(iterator_buffer&&) = delete;
method flush (line 543) | constexpr result<void> flush() noexcept {
method out (line 552) | constexpr std::back_insert_iterator<Container> out() noexcept {
method request_write_area (line 558) | constexpr result<std::span<char>> request_write_area(const size_t us...
class file_buffer (line 580) | class file_buffer final : public buffer {
method file_buffer (line 587) | constexpr explicit file_buffer(std::FILE* file) noexcept : file_{fil...
method file_buffer (line 591) | file_buffer(const file_buffer&) = delete;
method file_buffer (line 592) | file_buffer(file_buffer&&) = delete;
method file_buffer (line 593) | file_buffer& operator=(const file_buffer&) = delete;
method file_buffer (line 594) | file_buffer& operator=(file_buffer&&) = delete;
method flush (line 601) | result<void> flush() noexcept {
method reset (line 613) | constexpr void reset() noexcept {
method request_write_area (line 619) | result<std::span<char>> request_write_area(const size_t /*used*/, co...
class truncating_buffer (line 640) | class truncating_buffer final : public buffer {
method truncating_buffer (line 648) | constexpr explicit truncating_buffer(buffer& primary, size_t limit) ...
method truncating_buffer (line 652) | truncating_buffer(const truncating_buffer&) = delete;
method truncating_buffer (line 653) | truncating_buffer(truncating_buffer&&) = delete;
method truncating_buffer (line 654) | truncating_buffer& operator=(const truncating_buffer&) = delete;
method truncating_buffer (line 655) | truncating_buffer& operator=(truncating_buffer&&) = delete;
method count (line 662) | [[nodiscard]] constexpr size_t count() const noexcept {
method flush (line 669) | [[nodiscard]] constexpr result<void> flush() noexcept {
method request_write_area (line 683) | constexpr result<std::span<char>> request_write_area(const size_t /*...
type detail (line 701) | namespace detail {
function Container (line 365) | Container& get_container(std::back_insert_iterator<Container> it) no...
type get_value_type (line 376) | struct get_value_type {
type get_value_type<std::back_insert_iterator<Container>> (line 381) | struct get_value_type<std::back_insert_iterator<Container>> {
type get_value_type<std::ostreambuf_iterator<Char, Traits>> (line 387) | struct get_value_type<std::ostreambuf_iterator<Char, Traits>> {
function copy_str (line 396) | constexpr auto copy_str(InputIt it, InputIt end, OutputIt out) -> Ou...
class counting_buffer (line 709) | class counting_buffer final : public buffer {
method counting_buffer (line 712) | constexpr counting_buffer() noexcept = default;
method counting_buffer (line 713) | constexpr counting_buffer(const counting_buffer&) = delete;
method counting_buffer (line 714) | constexpr counting_buffer(counting_buffer&&) noexcept = delete;
method counting_buffer (line 715) | constexpr counting_buffer& operator=(const counting_buffer&) = del...
method counting_buffer (line 716) | constexpr counting_buffer& operator=(counting_buffer&&) noexcept =...
method count (line 723) | [[nodiscard]] constexpr size_t count() const noexcept {
method request_write_area (line 728) | constexpr result<std::span<char>> request_write_area(const size_t ...
FILE: include/emio/detail/args.hpp
type emio::detail (line 15) | namespace emio::detail {
class validation_arg (line 21) | class validation_arg {
method validation_arg (line 25) | explicit validation_arg(std::type_identity<T> /*unused*/) noexcept {
method validation_arg (line 30) | validation_arg(const validation_arg&) = delete;
method validation_arg (line 31) | validation_arg(validation_arg&&) = delete;
method validation_arg (line 32) | validation_arg& operator=(const validation_arg&) = delete;
method validation_arg (line 33) | validation_arg& operator=(validation_arg&&) = delete;
method validate (line 37) | result<void> validate(reader& format_rdr) const noexcept {
class concept_t (line 43) | class concept_t {
method concept_t (line 45) | concept_t() = default;
method concept_t (line 46) | concept_t(const concept_t&) = delete;
method concept_t (line 47) | concept_t(concept_t&&) = delete;
method concept_t (line 48) | concept_t& operator=(const concept_t&) = delete;
method concept_t (line 49) | concept_t& operator=(concept_t&&) = delete;
class model_t (line 58) | class model_t final : public concept_t {
method model_t (line 60) | explicit model_t() noexcept = default;
method model_t (line 61) | model_t(const model_t&) = delete;
method model_t (line 62) | model_t(model_t&&) = delete;
method model_t (line 63) | model_t& operator=(const model_t&) = delete;
method model_t (line 64) | model_t& operator=(model_t&&) = delete;
method validate (line 66) | result<void> validate(reader& format_rdr) const noexcept override {
class arg (line 81) | class arg {
method arg (line 85) | explicit arg(T& value) noexcept {
method arg (line 90) | arg(const arg&) = delete;
method arg (line 91) | arg(arg&&) = delete;
method arg (line 92) | arg& operator=(const arg&) = delete;
method arg (line 93) | arg& operator=(arg&&) = delete;
method process_arg (line 96) | result<void> process_arg(Input& input, reader& format_rdr) const noe...
class concept_t (line 102) | class concept_t {
method concept_t (line 104) | concept_t() = default;
method concept_t (line 105) | concept_t(const concept_t&) = delete;
method concept_t (line 106) | concept_t(concept_t&&) = delete;
method concept_t (line 107) | concept_t& operator=(const concept_t&) = delete;
method concept_t (line 108) | concept_t& operator=(concept_t&&) = delete;
class model_t (line 117) | class model_t final : public concept_t {
method model_t (line 119) | explicit model_t(T value) noexcept : value_{value} {}
method model_t (line 121) | model_t(const model_t&) = delete;
method model_t (line 122) | model_t(model_t&&) = delete;
method model_t (line 123) | model_t& operator=(const model_t&) = delete;
method model_t (line 124) | model_t& operator=(model_t&&) = delete;
method process_arg (line 126) | result<void> process_arg(Input& input, reader& format_rdr) const n...
class args_span (line 141) | class args_span {
method args_span (line 143) | args_span() = default;
method args_span (line 144) | args_span(const args_span&) = delete;
method args_span (line 145) | args_span(args_span&&) = delete;
method args_span (line 146) | args_span& operator=(const args_span&) = delete;
method args_span (line 147) | args_span& operator=(args_span&&) = delete;
method get_args (line 150) | [[nodiscard]] std::span<const Arg> get_args() const noexcept {
method args_span (line 155) | args_span(std::span<const Arg> args) : args_{args} {}
class args_span_with_str (line 162) | class args_span_with_str : public args_span<Arg> {
method args_span_with_str (line 164) | args_span_with_str() = default;
method args_span_with_str (line 165) | args_span_with_str(const args_span_with_str&) = delete;
method args_span_with_str (line 166) | args_span_with_str(args_span_with_str&&) = delete;
method args_span_with_str (line 167) | args_span_with_str& operator=(const args_span_with_str&) = delete;
method args_span_with_str (line 168) | args_span_with_str& operator=(args_span_with_str&&) = delete;
method get_str (line 175) | [[nodiscard]] result<std::string_view> get_str() const noexcept {
method is_plain_str (line 184) | [[nodiscard]] constexpr bool is_plain_str() const noexcept {
method empty (line 192) | [[nodiscard]] constexpr bool empty() const noexcept {
method args_span_with_str (line 198) | args_span_with_str(const validated_string_storage& str, std::span<co...
class args_storage (line 206) | class args_storage : public args_span_with_str<Arg> {
method args_storage (line 210) | args_storage(const validated_string_storage& str, Args&&... args) no...
method args_storage (line 213) | args_storage(const args_storage&) = delete;
method args_storage (line 214) | args_storage(args_storage&&) = delete;
method args_storage (line 215) | args_storage& operator=(const args_storage&) = delete;
method args_storage (line 216) | args_storage& operator=(args_storage&&) = delete;
class validation_args_storage (line 224) | class validation_args_storage : public args_span<Arg> {
method validation_args_storage (line 227) | validation_args_storage(Args&&... args) noexcept
method validation_args_storage (line 230) | validation_args_storage(const validation_args_storage&) = delete;
method validation_args_storage (line 231) | validation_args_storage(validation_args_storage&&) = delete;
method validation_args_storage (line 232) | validation_args_storage& operator=(const validation_args_storage&) =...
method validation_args_storage (line 233) | validation_args_storage& operator=(validation_args_storage&&) = delete;
function make_validation_args (line 241) | validation_args_storage<T, sizeof...(Args)> make_validation_args() noe...
FILE: include/emio/detail/bignum.hpp
type emio::detail (line 20) | namespace emio::detail {
type carrying_add_result_t (line 22) | struct carrying_add_result_t {
function carrying_add_result_t (line 30) | inline constexpr carrying_add_result_t carrying_add(uint32_t a, uint32...
type borrowing_sub_result_t (line 38) | struct borrowing_sub_result_t {
function borrowing_sub_result_t (line 46) | inline constexpr borrowing_sub_result_t borrowing_sub(uint32_t a, uint...
type carrying_mul_result_t (line 54) | struct carrying_mul_result_t {
function carrying_mul_result_t (line 62) | inline constexpr carrying_mul_result_t carrying_mul(uint32_t a, uint32...
class bignum (line 70) | class bignum {
method bignum (line 74) | static constexpr bignum from(size_t sz, const std::array<uint32_t, m...
method bignum (line 81) | constexpr explicit bignum() noexcept = default;
method bignum (line 86) | constexpr explicit bignum(T v) noexcept : base_{{v}} {}
method bignum (line 91) | constexpr explicit bignum(T v) noexcept : base_{{static_cast<uint32_...
method digits (line 98) | constexpr std::span<uint32_t> digits() noexcept {
method get_bit (line 104) | [[nodiscard]] constexpr uint8_t get_bit(size_t i) const noexcept {
method is_zero (line 112) | [[nodiscard]] constexpr bool is_zero() const noexcept {
method bignum (line 120) | constexpr bignum& add_small(uint32_t other) noexcept {
method bignum (line 124) | constexpr bignum& add_small_at(size_t index, uint32_t other) noexcept {
method bignum (line 138) | constexpr bignum& add(const bignum& other) noexcept {
method bignum (line 153) | constexpr bignum& sub_small(uint32_t other) noexcept {
method bignum (line 169) | constexpr bignum& sub(const bignum& other) noexcept {
method bignum (line 190) | constexpr bignum& mul_small(uint32_t other) noexcept {
method bignum (line 194) | constexpr bignum& muladd_small(uint32_t other, uint32_t carry) noexc...
method bignum (line 207) | [[nodiscard]] bignum mul(const bignum& other) const noexcept {
method bignum (line 225) | constexpr bignum& mul_digits(std::span<const uint32_t> other) noexce...
method bignum (line 245) | constexpr bignum& mul_pow5(size_t k) noexcept {
method div_rem_small (line 265) | constexpr uint32_t div_rem_small(uint32_t other) noexcept {
method bignum (line 277) | constexpr bignum& mul_pow2(size_t exp) noexcept {
FILE: include/emio/detail/bitset.hpp
type emio::detail (line 14) | namespace emio::detail {
class bitset (line 21) | class bitset {
method all (line 32) | [[nodiscard]] constexpr bool all() const noexcept {
method all_first (line 51) | [[nodiscard, gnu::noinline]] constexpr bool all_first(size_t n) cons...
method size (line 80) | [[nodiscard]] constexpr size_t size() const noexcept {
method set (line 88) | constexpr void set(size_t pos) noexcept {
method word_t (line 98) | static constexpr word_t get_high_word_mask() noexcept {
FILE: include/emio/detail/conversion.hpp
type emio::detail (line 19) | namespace emio::detail {
function isalpha (line 21) | constexpr bool isalpha(char c) {
function isdigit (line 25) | constexpr bool isdigit(char c) {
function is_valid_number_base (line 29) | constexpr bool is_valid_number_base(const int base) noexcept {
function char_to_digit (line 33) | constexpr std::optional<int> char_to_digit(const char c, const int bas...
function digit_to_char (line 51) | constexpr char digit_to_char(const int digit, bool upper) noexcept {
function count_digits_10 (line 65) | constexpr size_t count_digits_10(T number) noexcept {
function count_digits (line 90) | constexpr size_t count_digits(T number) noexcept {
function get_number_of_digits (line 114) | constexpr size_t get_number_of_digits(T number, int base) noexcept {
function is_negative (line 138) | constexpr bool is_negative(T value) noexcept {
function num_bits (line 147) | constexpr int num_bits() noexcept {
function integer_upcast (line 162) | constexpr auto integer_upcast(T integer) noexcept {
function to_absolute (line 167) | constexpr uint32_or_64<T> to_absolute(T number) noexcept {
function to_unsigned (line 181) | constexpr std::make_unsigned_t<T> to_unsigned(T number) noexcept {
function to_signed (line 186) | constexpr std::make_signed_t<T> to_signed(T number) noexcept {
function copy2 (line 202) | inline constexpr void copy2(Char* dst, const char* src) noexcept {
function unchecked_substr (line 275) | constexpr std::string_view unchecked_substr(const std::string_view& st...
function equal_n (line 306) | [[nodiscard]] inline constexpr bool equal_n(const char* a, const char*...
function sv (line 323) | inline consteval std::string_view sv(std::string_view sv) noexcept {
function begin (line 339) | constexpr auto begin(T& obj) noexcept {
function end (line 349) | constexpr auto end(T& obj) noexcept {
FILE: include/emio/detail/ct_vector.hpp
type emio::detail (line 16) | namespace emio::detail {
class ct_vector (line 24) | class ct_vector {
method ct_vector (line 26) | constexpr ct_vector() noexcept {
method ct_vector (line 32) | constexpr ct_vector(const ct_vector& other) : ct_vector() {
method ct_vector (line 37) | constexpr ct_vector(ct_vector&& other) noexcept : ct_vector() {
method ct_vector (line 53) | constexpr ct_vector& operator=(const ct_vector& other) {
method ct_vector (line 62) | constexpr ct_vector& operator=(ct_vector&& other) noexcept {
method reserve (line 94) | constexpr void reserve(size_t new_size) noexcept {
method clear (line 118) | constexpr void clear() noexcept {
method capacity (line 122) | [[nodiscard]] constexpr size_t capacity() const noexcept {
method size (line 126) | [[nodiscard]] constexpr size_t size() const noexcept {
method Char (line 130) | [[nodiscard]] constexpr Char* data() noexcept {
method Char (line 134) | [[nodiscard]] constexpr const Char* data() const noexcept {
method hold_external (line 139) | [[nodiscard]] constexpr bool hold_external() const noexcept {
FILE: include/emio/detail/format/args.hpp
type emio::detail::format (line 12) | namespace emio::detail::format {
type format_arg_trait (line 15) | struct format_arg_trait {
method validate (line 18) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method process_arg (line 22) | static constexpr result<void> process_arg(writer& out, reader& forma...
FILE: include/emio/detail/format/decode.hpp
type emio::detail::format (line 18) | namespace emio::detail::format {
type finite_result_t (line 20) | struct finite_result_t {
type category (line 28) | enum class category { zero, finite, infinity, nan }
type decode_result_t (line 30) | struct decode_result_t {
function decode_result_t (line 36) | inline constexpr decode_result_t decode(double value) noexcept {
FILE: include/emio/detail/format/dragon.hpp
type emio::detail::format (line 25) | namespace emio::detail::format {
function estimate_scaling_factor (line 27) | inline constexpr int16_t estimate_scaling_factor(uint64_t mant, int16_...
function round_up (line 35) | inline constexpr std::optional<char> round_up(std::span<char> d) noexc...
type format_fp_result_t (line 60) | struct format_fp_result_t {
type format_exact_mode (line 65) | enum class format_exact_mode { significand_digits, decimal_point }
function format_fp_result_t (line 67) | inline constexpr format_fp_result_t format_exact(const finite_result_t...
function format_fp_result_t (line 207) | inline constexpr format_fp_result_t format_shortest(const finite_resul...
FILE: include/emio/detail/format/format_to.hpp
type emio (line 16) | namespace emio {
type detail::format (line 18) | namespace detail::format {
type format_trait (line 20) | struct format_trait {
method validate_string (line 22) | [[nodiscard]] static constexpr bool validate_string(std::string_vi...
function vformat_to (line 39) | inline result<void> vformat_to(buffer& buf, const format_args& args)...
function format_to (line 50) | constexpr result<void> format_to(buffer& buf, const format_string<Ar...
type unified_type<T> (line 94) | struct unified_type<T> {
class formatter<detail::format::format_args> (line 66) | class formatter<detail::format::format_args> {
method validate (line 68) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 72) | static constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 76) | static result<void> format(writer& out, const detail::format::format...
class formatter<T> (line 88) | class formatter<T> : public formatter<detail::format::format_args> {}
type detail::format (line 90) | namespace detail::format {
type format_trait (line 20) | struct format_trait {
method validate_string (line 22) | [[nodiscard]] static constexpr bool validate_string(std::string_vi...
function vformat_to (line 39) | inline result<void> vformat_to(buffer& buf, const format_args& args)...
function format_to (line 50) | constexpr result<void> format_to(buffer& buf, const format_string<Ar...
type unified_type<T> (line 94) | struct unified_type<T> {
FILE: include/emio/detail/format/formatter.hpp
type emio (line 15) | namespace emio {
class formatter (line 18) | class formatter
type detail::format (line 20) | namespace detail::format {
type alternate_form (line 22) | namespace alternate_form {
function write_padding_left (line 38) | inline constexpr result<void> write_padding_left(writer& out, format...
function write_padding_right (line 55) | inline constexpr result<void> write_padding_right(writer& out, forma...
function write_padded (line 63) | constexpr result<void> write_padded(writer& out, format_specs& specs...
function make_write_int_options (line 72) | inline constexpr result<std::pair<std::string_view, writer::write_in...
function try_write_sign (line 110) | inline constexpr result<char> try_write_sign(writer& out, const form...
function try_write_prefix (line 124) | inline constexpr result<std::string_view> try_write_prefix(writer& o...
function write_arg (line 139) | constexpr result<void> write_arg(writer& out, format_specs& specs, c...
function write_non_finite (line 181) | inline constexpr result<void> write_non_finite(writer& out, bool upp...
type fp_format (line 191) | enum class fp_format : uint8_t {
type fp_format_specs (line 198) | struct fp_format_specs {
function fp_format_specs (line 205) | inline constexpr fp_format_specs parse_fp_format_specs(const format_...
function write_decimal (line 262) | inline constexpr result<void> write_decimal(writer& out, format_spec...
function format_fp_result_t (line 415) | inline constexpr format_fp_result_t format_decimal(buffer& buffer, c...
function format_and_write_decimal (line 441) | inline constexpr result<void> format_and_write_decimal(writer& out, ...
function write_arg (line 468) | constexpr result<void> write_arg(writer& out, format_specs& specs, c...
function write_arg (line 472) | inline constexpr result<void> write_arg(writer& out, format_specs& s...
function write_arg (line 489) | constexpr result<void> write_arg(writer& out, format_specs& specs, c...
function write_arg (line 510) | constexpr result<void> write_arg(writer& out, format_specs& specs, A...
function write_arg (line 522) | constexpr result<void> write_arg(writer& out, format_specs& specs, A...
function validate_format_specs (line 542) | inline constexpr result<void> validate_format_specs(reader& format_r...
function parse_format_specs (line 623) | inline constexpr result<void> parse_format_specs(reader& format_rdr,...
function check_integral_specs (line 686) | inline constexpr result<void> check_integral_specs(const format_spec...
function check_unsigned_specs (line 706) | inline constexpr result<void> check_unsigned_specs(const format_spec...
function check_bool_specs (line 713) | inline constexpr result<void> check_bool_specs(const format_specs& s...
function check_char_specs (line 723) | inline constexpr result<void> check_char_specs(const format_specs& s...
function check_pointer_specs (line 733) | inline constexpr result<void> check_pointer_specs(const format_specs...
function check_floating_point_specs (line 743) | inline constexpr result<void> check_floating_point_specs(const forma...
function check_string_specs (line 764) | inline constexpr result<void> check_string_specs(const format_specs&...
function validate_trait (line 805) | constexpr result<void> validate_trait(reader& format_rdr) {
type unified_type (line 836) | struct unified_type
type unified_type (line 839) | struct unified_type {
type unified_type<T> (line 845) | struct unified_type<T> {
type unified_type<T> (line 851) | struct unified_type<T> {
type unified_type<T> (line 857) | struct unified_type<T> {
type unified_type<T> (line 863) | struct unified_type<T> {
type unified_type<T> (line 869) | struct unified_type<T> {
FILE: include/emio/detail/format/parser.hpp
type emio::detail::format (line 12) | namespace emio::detail::format {
class format_parser (line 14) | class format_parser final : public parser<format_parser, input_validat...
method format_parser (line 16) | constexpr explicit format_parser(writer& out, reader& format_rdr) no...
method format_parser (line 19) | format_parser(const format_parser&) = delete;
method format_parser (line 20) | format_parser(format_parser&&) = delete;
method format_parser (line 21) | format_parser& operator=(const format_parser&) = delete;
method format_parser (line 22) | format_parser& operator=(format_parser&&) = delete;
method process (line 25) | constexpr result<void> process(const std::string_view& str) noexcept...
method process_arg (line 29) | result<void> process_arg(const format_arg& arg) noexcept {
method process_arg (line 34) | constexpr result<void> process_arg(const Arg& arg) noexcept {
class format_specs_checker (line 52) | class format_specs_checker final : public parser<format_specs_checker,...
method format_specs_checker (line 56) | format_specs_checker(const format_specs_checker& other) = delete;
method format_specs_checker (line 57) | format_specs_checker(format_specs_checker&& other) = delete;
method format_specs_checker (line 58) | format_specs_checker& operator=(const format_specs_checker& other) =...
method format_specs_checker (line 59) | format_specs_checker& operator=(format_specs_checker&& other) = delete;
method process_arg (line 62) | result<void> process_arg(const format_validation_arg& arg) noexcept {
method process_arg (line 67) | constexpr result<void> process_arg(std::type_identity<Arg> /*unused*...
FILE: include/emio/detail/format/ranges.hpp
type emio::detail::format (line 17) | namespace emio::detail::format {
type is_span (line 52) | struct is_span : std::false_type {}
type is_span<std::span<T, N>> (line 55) | struct is_span<std::span<T, N>> : std::true_type {}
type ranges_specs (line 64) | struct ranges_specs {
function maybe_set_debug_format (line 72) | constexpr void maybe_set_debug_format(Formatter& f, bool set) noexcept {
function maybe_set_debug_format (line 77) | constexpr void maybe_set_debug_format(Formatter& /*unused*/, ...) noex...
function has_tuple_element_unpack (line 91) | constexpr auto has_tuple_element_unpack(std::index_sequence<Ns...> /*u...
function is_formattable_unpack (line 102) | constexpr auto is_formattable_unpack(std::index_sequence<Ns...> /*unus...
FILE: include/emio/detail/format/specs.hpp
type emio::detail::format (line 11) | namespace emio::detail::format {
type alignment (line 33) | enum class alignment : uint8_t { none, left, center, right }
type format_specs (line 35) | struct format_specs {
FILE: include/emio/detail/misc.hpp
type emio::detail (line 11) | namespace emio::detail {
type always_false (line 14) | struct always_false : std::false_type {}
type overload (line 20) | struct overload : Ts... {
FILE: include/emio/detail/parser.hpp
type emio::detail (line 18) | namespace emio::detail {
type input_validation (line 23) | enum class input_validation { enabled, disabled }
class parser_base (line 28) | class parser_base {
method parser_base (line 30) | constexpr explicit parser_base(reader& format_rdr) noexcept : format...
method parser_base (line 32) | parser_base(const parser_base&) = delete;
method parser_base (line 33) | parser_base(parser_base&&) = delete;
method parser_base (line 34) | parser_base& operator=(const parser_base&) = delete;
method parser_base (line 35) | parser_base& operator=(parser_base&&) = delete;
method parse (line 39) | constexpr result<void> parse(uint8_t& arg_nbr) noexcept {
method parse_replacement_field (line 67) | constexpr result<void> parse_replacement_field(uint8_t& arg_nbr) noe...
method parse_field_name (line 82) | constexpr result<void> parse_field_name(uint8_t& arg_nbr) noexcept {
class parser_base<input_validation::disabled> (line 106) | class parser_base<input_validation::disabled> {
method parser_base (line 108) | constexpr explicit parser_base(reader& format_rdr) noexcept : format...
method parser_base (line 110) | parser_base(const parser_base& other) = delete;
method parser_base (line 111) | parser_base(parser_base&& other) = delete;
method parser_base (line 112) | parser_base& operator=(const parser_base& other) = delete;
method parser_base (line 113) | parser_base& operator=(parser_base&& other) = delete;
method parse (line 117) | constexpr result<void> parse(uint8_t& arg_nbr) noexcept {
method parse_replacement_field (line 156) | constexpr result<void> parse_replacement_field(uint8_t& arg_nbr) noe...
method parse_field_name (line 166) | constexpr void parse_field_name(uint8_t& arg_nbr) noexcept {
type related_format_args (line 184) | struct related_format_args {
class parser (line 192) | class parser : public parser_base<Validation> {
method parser (line 196) | parser(const parser&) = delete;
method parser (line 197) | parser(parser&&) = delete;
method parser (line 198) | parser& operator=(const parser&) = delete;
method parser (line 199) | parser& operator=(parser&&) = delete;
method apply (line 203) | result<void> apply(uint8_t arg_nbr, const related_format_args<Arg>& ...
method apply (line 208) | constexpr result<void> apply(uint8_t /*arg_pos*/) noexcept {
method apply (line 213) | constexpr result<void> apply(uint8_t arg_pos, Arg& arg, Args&... arg...
function validate (line 226) | constexpr bool validate(std::string_view str, const size_t arg_cnt, co...
function parse (line 251) | constexpr result<void> parse(std::string_view str, T& input, Args&&......
FILE: include/emio/detail/predef.hpp
type emio::detail (line 9) | namespace emio::detail {
FILE: include/emio/detail/scan/args.hpp
type emio::detail::scan (line 12) | namespace emio::detail::scan {
type scan_arg_trait (line 15) | struct scan_arg_trait {
method validate (line 18) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method process_arg (line 35) | static constexpr result<void> process_arg(reader& in, reader& format...
FILE: include/emio/detail/scan/parser.hpp
type emio::detail::scan (line 12) | namespace emio::detail::scan {
class scan_parser (line 14) | class scan_parser final : public parser<scan_parser, input_validation:...
method scan_parser (line 16) | constexpr explicit scan_parser(reader& in, reader& format_rdr) noexcept
method scan_parser (line 19) | scan_parser(const scan_parser&) = delete;
method scan_parser (line 20) | scan_parser(scan_parser&&) = delete;
method scan_parser (line 21) | scan_parser& operator=(const scan_parser&) = delete;
method scan_parser (line 22) | scan_parser& operator=(scan_parser&&) = delete;
method process (line 25) | constexpr result<void> process(const std::string_view& str) noexcept...
method process_arg (line 29) | result<void> process_arg(const scan_arg& arg) noexcept {
method process_arg (line 34) | constexpr result<void> process_arg(Arg& arg) noexcept {
class scan_specs_checker (line 52) | class scan_specs_checker final : public parser<scan_specs_checker, inp...
method scan_specs_checker (line 56) | scan_specs_checker(const scan_specs_checker& other) = delete;
method scan_specs_checker (line 57) | scan_specs_checker(scan_specs_checker&& other) = delete;
method scan_specs_checker (line 58) | scan_specs_checker& operator=(const scan_specs_checker& other) = del...
method scan_specs_checker (line 59) | scan_specs_checker& operator=(scan_specs_checker&& other) = delete;
method process_arg (line 62) | result<void> process_arg(const scan_validation_arg& arg) noexcept {
method process_arg (line 67) | constexpr result<void> process_arg(std::type_identity<Arg> /*unused*...
FILE: include/emio/detail/scan/scan_from.hpp
type emio::detail::scan (line 15) | namespace emio::detail::scan {
type scan_trait (line 17) | struct scan_trait {
method validate_string (line 19) | [[nodiscard]] static constexpr bool validate_string(std::string_view...
function vscan_from (line 35) | inline result<void> vscan_from(reader& in, const scan_args& args) noex...
function scan_from (line 44) | constexpr result<void> scan_from(reader& in, const format_string<Args....
FILE: include/emio/detail/scan/scanner.hpp
type emio (line 12) | namespace emio {
class scanner (line 15) | class scanner
type detail::scan (line 17) | namespace detail::scan {
function disallow_sign (line 23) | inline constexpr result<void> disallow_sign(reader& in) noexcept {
function determine_base (line 31) | inline constexpr result<int> determine_base(reader in) noexcept {
function get_base (line 52) | inline constexpr int get_base(char type) noexcept {
function parse_alternate_form (line 69) | inline constexpr result<void> parse_alternate_form(reader& in, int b...
function read_arg (line 93) | constexpr result<void> read_arg(reader& original_in, const format_sp...
function read_arg (line 138) | constexpr result<void> read_arg(reader& in, const format_specs& /*un...
function read_string_complex (line 143) | inline constexpr result<void> read_string_complex(reader& in, const ...
function read_string (line 203) | inline constexpr result<void> read_string(reader& in, format_specs& ...
function validate_format_specs (line 243) | inline constexpr result<void> validate_format_specs(reader& format_r...
function parse_format_specs (line 272) | inline constexpr result<void> parse_format_specs(reader& format_rdr,...
function check_char_specs (line 294) | inline constexpr result<void> check_char_specs(const format_specs& s...
function check_integral_specs (line 301) | inline constexpr result<void> check_integral_specs(const format_spec...
function check_string_specs (line 314) | inline constexpr result<void> check_string_specs(const format_specs&...
FILE: include/emio/detail/scan/specs.hpp
type emio::detail::scan (line 13) | namespace emio::detail::scan {
type format_specs (line 18) | struct format_specs {
FILE: include/emio/detail/utf.hpp
type emio::detail (line 18) | namespace emio::detail {
function needs_escape (line 20) | inline constexpr bool needs_escape(uint32_t cp) noexcept {
function count_size_when_escaped (line 24) | inline constexpr size_t count_size_when_escaped(std::string_view sv) n...
class write_escaped_helper (line 41) | class write_escaped_helper {
method write_escaped_helper (line 43) | constexpr write_escaped_helper(std::string_view sv) noexcept
method write_escaped (line 46) | [[nodiscard]] constexpr size_t write_escaped(std::span<char> area) n...
function write_str_escaped (line 126) | inline constexpr result<void> write_str_escaped(buffer& buf, std::stri...
FILE: include/emio/detail/validated_string.hpp
type emio (line 15) | namespace emio {
type detail (line 17) | namespace detail {
class runtime_string (line 22) | class runtime_string {
method runtime_string (line 27) | constexpr runtime_string() = default;
method runtime_string (line 30) | constexpr runtime_string(std::nullptr_t) = delete;
method runtime_string (line 31) | constexpr runtime_string(int) = delete;
method runtime_string (line 34) | constexpr runtime_string(std::string&&) = delete;
method runtime_string (line 43) | constexpr explicit runtime_string(const S& str) : str_{str} {}
method get (line 49) | [[nodiscard]] constexpr std::string_view get() const noexcept {
class valid_string (line 58) | class valid_string
method from (line 120) | static constexpr result<valid_string<Trait, Args...>> from(const S...
method valid_string (line 135) | consteval valid_string(const S& s) noexcept : validated_string<Tra...
method valid_string (line 140) | constexpr explicit valid_string(const validated_string_storage& st...
class validated_string (line 66) | class validated_string : public validated_string_storage {
method validated_string (line 75) | consteval validated_string(const S& s) noexcept
method validated_string (line 87) | constexpr validated_string(const runtime_string& s) noexcept
method as_valid (line 95) | constexpr result<valid_string<Trait, Args...>> as_valid() const no...
method validated_string (line 103) | constexpr explicit validated_string(const validated_string_storage...
class valid_string (line 111) | class valid_string : public validated_string<Trait, Args...> {
method from (line 120) | static constexpr result<valid_string<Trait, Args...>> from(const S...
method valid_string (line 135) | consteval valid_string(const S& s) noexcept : validated_string<Tra...
method valid_string (line 140) | constexpr explicit valid_string(const validated_string_storage& st...
function runtime_string (line 154) | inline constexpr runtime_string runtime(const std::string_view& s) noe...
FILE: include/emio/detail/validated_string_storage.hpp
type emio::detail (line 14) | namespace emio::detail {
function check_if_plain_string (line 16) | inline constexpr bool check_if_plain_string(const std::string_view& s)...
class validated_string_storage (line 25) | class validated_string_storage {
method validated_string_storage (line 28) | static constexpr validated_string_storage from(const std::string_vie...
method validated_string_storage (line 40) | constexpr validated_string_storage() noexcept = default;
method is_plain_str (line 46) | [[nodiscard]] constexpr bool is_plain_str() const noexcept {
method empty (line 54) | [[nodiscard]] constexpr bool empty() const noexcept {
method get (line 62) | constexpr result<std::string_view> get() const noexcept {
method validated_string_storage (line 68) | constexpr validated_string_storage(const std::pair<bool, result<std:...
FILE: include/emio/format.hpp
type emio (line 14) | namespace emio {
function make_format_args (line 42) | [[nodiscard]] detail::args_storage<detail::format::format_arg, sizeof....
function vformatted_size (line 53) | inline result<size_t> vformatted_size(const format_args& args) noexcept {
function formatted_size (line 68) | [[nodiscard]] constexpr size_t formatted_size(const valid_format_strin...
function formatted_size (line 87) | [[nodiscard]] constexpr result<size_t> formatted_size(const valid_form...
function formatted_size (line 104) | constexpr result<size_t> formatted_size(const T& format_str, const Arg...
function vformat_to (line 117) | inline result<void> vformat_to(buffer& buf, const format_args& args) n...
function vformat_to (line 128) | inline result<void> vformat_to(writer& out, const format_args& args) n...
function vformat_to (line 142) | constexpr result<OutputIt> vformat_to(OutputIt out, const format_args&...
function format_to (line 156) | constexpr result<void> format_to(buffer& buf, const emio::format_strin...
function format_to (line 174) | constexpr result<void> format_to(writer& out, const emio::format_strin...
function format_to (line 193) | constexpr result<OutputIt> format_to(OutputIt out, const emio::format_...
function vformat (line 211) | inline result<std::string> vformat(const format_args& args) noexcept {
function format (line 228) | [[nodiscard]] std::string format(const emio::valid_format_string<Args....
function format (line 241) | [[nodiscard]] result<std::string> format(const emio::valid_format_stri...
function format (line 254) | result<std::string> format(T format_str, const Args&... args) noexcept {
type format_to_n_result (line 264) | struct format_to_n_result {
function vformat_to_n (line 279) | result<format_to_n_result<OutputIt>> vformat_to_n(OutputIt out, std::i...
function vformat_to_n (line 299) | result<size_t> vformat_to_n(buffer& buf, size_t n, const format_args& ...
function format_to_n (line 317) | constexpr result<format_to_n_result<OutputIt>> format_to_n(OutputIt ou...
function format_to_n (line 343) | constexpr result<size_t> format_to_n(buffer& buf, size_t n, const emio...
function vprint (line 361) | inline result<void> vprint(std::FILE* file, const format_args& args) n...
function print (line 379) | void print(const emio::valid_format_string<Args...>& format_str,
function print (line 392) | result<void> print(const emio::valid_format_string<Args...>& format_st...
function print (line 404) | result<void> print(const T& format_str, const Args&... args) noexcept {
function print (line 416) | result<void> print(std::FILE* file, const emio::format_string<Args...>...
function vprintln (line 427) | inline result<void> vprintln(std::FILE* file, const format_args& args)...
function println (line 447) | void println(const emio::valid_format_string<Args...>& format_str,
function println (line 461) | result<void> println(const emio::valid_format_string<Args...>& format_...
function println (line 474) | result<void> println(const T& format_str, const Args&... args) noexcept {
function println (line 486) | result<void> println(std::FILE* file, const emio::format_string<Args.....
FILE: include/emio/formatter.hpp
type emio (line 13) | namespace emio {
class formatter (line 35) | class formatter {
method formatter (line 38) | formatter() = delete;
method validate (line 46) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 55) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 65) | constexpr result<void> format(writer& out, const T& arg) const noexc...
class formatter<T> (line 91) | class formatter<T> {
method validate (line 93) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 117) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 121) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method set_debug_format (line 131) | constexpr void set_debug_format(bool enabled) noexcept
method set_width (line 146) | constexpr void set_width(int32_t width) noexcept {
method set_precision (line 155) | constexpr void set_precision(int32_t precision) noexcept {
method format (line 179) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method format (line 203) | constexpr result<void> format(writer& out, const T& arg) const noexc...
class formatter<T> (line 169) | class formatter<T> : public formatter<detail::format::unified_type_t<T...
method validate (line 93) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 117) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 121) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method set_debug_format (line 131) | constexpr void set_debug_format(bool enabled) noexcept
method set_width (line 146) | constexpr void set_width(int32_t width) noexcept {
method set_precision (line 155) | constexpr void set_precision(int32_t precision) noexcept {
method format (line 179) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method format (line 203) | constexpr result<void> format(writer& out, const T& arg) const noexc...
class formatter<T> (line 177) | class formatter<T> : public formatter<std::underlying_type_t<T>> {
method validate (line 93) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 117) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 121) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method set_debug_format (line 131) | constexpr void set_debug_format(bool enabled) noexcept
method set_width (line 146) | constexpr void set_width(int32_t width) noexcept {
method set_precision (line 155) | constexpr void set_precision(int32_t precision) noexcept {
method format (line 179) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method format (line 203) | constexpr result<void> format(writer& out, const T& arg) const noexc...
class formatter<std::byte> (line 188) | class formatter<std::byte> : public formatter<uint8_t> {
method format (line 190) | constexpr result<void> format(writer& out, const std::byte& arg) con...
class formatter<T> (line 201) | class formatter<T> : public formatter<detail::format::format_as_return...
method validate (line 93) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 117) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 121) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method set_debug_format (line 131) | constexpr void set_debug_format(bool enabled) noexcept
method set_width (line 146) | constexpr void set_width(int32_t width) noexcept {
method set_precision (line 155) | constexpr void set_precision(int32_t precision) noexcept {
method format (line 179) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method format (line 203) | constexpr result<void> format(writer& out, const T& arg) const noexc...
type detail (line 208) | namespace detail {
type format_spec_with_value (line 211) | struct format_spec_with_value
type format_spec_with_value (line 247) | struct format_spec_with_value {
type format_spec (line 218) | struct format_spec {
type detail (line 241) | namespace detail {
type format_spec_with_value (line 211) | struct format_spec_with_value
type format_spec_with_value (line 247) | struct format_spec_with_value {
class formatter<detail::format_spec_with_value<T>> (line 264) | class formatter<detail::format_spec_with_value<T>> {
method validate (line 266) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 270) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 274) | constexpr result<void> format(writer& out, const detail::format_spec...
method F (line 285) | static constexpr F& get_core_formatter(F& formatter) noexcept {
method overwrite_spec (line 295) | constexpr void overwrite_spec(const format_spec& spec) noexcept {
function ptr (line 316) | constexpr auto ptr(T p) noexcept {
FILE: include/emio/iterator.hpp
type emio (line 11) | namespace emio {
class truncating_iterator_base (line 18) | class truncating_iterator_base {
method truncating_iterator_base (line 29) | constexpr truncating_iterator_base() = default;
method truncating_iterator_base (line 36) | constexpr truncating_iterator_base(OutputIt out, size_t limit)
method OutputIt (line 43) | constexpr OutputIt out() const {
method count (line 51) | [[nodiscard]] constexpr std::iter_difference_t<OutputIt> count() con...
class truncating_iterator (line 67) | class truncating_iterator
class truncating_iterator<OutputIt> (line 79) | class truncating_iterator<OutputIt> : public truncating_iterator_base<...
method truncating_iterator (line 84) | constexpr truncating_iterator& operator=(T val) {
method truncating_iterator (line 92) | constexpr truncating_iterator& operator++() {
method truncating_iterator (line 95) | constexpr truncating_iterator& operator++(int) {
method truncating_iterator (line 98) | constexpr truncating_iterator& operator*() {
method truncating_iterator (line 115) | constexpr truncating_iterator& operator++() {
method truncating_iterator (line 123) | constexpr truncating_iterator operator++(int) {
method value_type (line 129) | constexpr value_type& operator*() const {
class truncating_iterator<OutputIt> (line 109) | class truncating_iterator<OutputIt> : public truncating_iterator_base<...
method truncating_iterator (line 84) | constexpr truncating_iterator& operator=(T val) {
method truncating_iterator (line 92) | constexpr truncating_iterator& operator++() {
method truncating_iterator (line 95) | constexpr truncating_iterator& operator++(int) {
method truncating_iterator (line 98) | constexpr truncating_iterator& operator*() {
method truncating_iterator (line 115) | constexpr truncating_iterator& operator++() {
method truncating_iterator (line 123) | constexpr truncating_iterator operator++(int) {
method value_type (line 129) | constexpr value_type& operator*() const {
type detail (line 140) | namespace detail {
type get_value_type (line 143) | struct get_value_type
type get_value_type<truncating_iterator<Iterator>> (line 146) | struct get_value_type<truncating_iterator<Iterator>> : get_value_typ...
FILE: include/emio/ranges.hpp
type emio (line 14) | namespace emio {
class formatter<T> (line 22) | class formatter<T> {
method validate (line 24) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method formatter (line 39) | constexpr formatter() noexcept
method formatter (line 43) | constexpr formatter() noexcept
method formatter (line 50) | constexpr formatter() noexcept
method set_separator (line 54) | constexpr void set_separator(std::string_view separator) noexcept {
method set_brackets (line 58) | constexpr void set_brackets(std::string_view opening_bracket, std::s...
method parse (line 63) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 78) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method formatter (line 120) | constexpr formatter() : specs_{detail::sv("("), detail::sv(")"), det...
method set_separator (line 122) | constexpr void set_separator(std::string_view separator) noexcept {
method set_brackets (line 126) | constexpr void set_brackets(std::string_view opening_bracket, std::s...
method validate (line 131) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 146) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 162) | constexpr result<void> format(writer& out, const T& args) const noex...
method validate_for_each (line 171) | static constexpr result<void> validate_for_each(std::index_sequence<...
method validate_for_each (line 196) | static constexpr result<void> validate_for_each(std::index_sequence<...
method parse_for_each (line 201) | constexpr result<void> parse_for_each(std::index_sequence<Ns...> /*u...
method parse_for_each (line 221) | constexpr result<void> parse_for_each(std::index_sequence<> /*unused...
method format_for_each (line 231) | constexpr result<void> format_for_each(std::index_sequence<N, Ns...>...
method format_for_each (line 252) | constexpr result<void> format_for_each(std::index_sequence<> /*unuse...
class formatter<T> (line 110) | class formatter<T> : public formatter<std::span<const detail::format::...
method validate (line 24) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method formatter (line 39) | constexpr formatter() noexcept
method formatter (line 43) | constexpr formatter() noexcept
method formatter (line 50) | constexpr formatter() noexcept
method set_separator (line 54) | constexpr void set_separator(std::string_view separator) noexcept {
method set_brackets (line 58) | constexpr void set_brackets(std::string_view opening_bracket, std::s...
method parse (line 63) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 78) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method formatter (line 120) | constexpr formatter() : specs_{detail::sv("("), detail::sv(")"), det...
method set_separator (line 122) | constexpr void set_separator(std::string_view separator) noexcept {
method set_brackets (line 126) | constexpr void set_brackets(std::string_view opening_bracket, std::s...
method validate (line 131) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 146) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 162) | constexpr result<void> format(writer& out, const T& args) const noex...
method validate_for_each (line 171) | static constexpr result<void> validate_for_each(std::index_sequence<...
method validate_for_each (line 196) | static constexpr result<void> validate_for_each(std::index_sequence<...
method parse_for_each (line 201) | constexpr result<void> parse_for_each(std::index_sequence<Ns...> /*u...
method parse_for_each (line 221) | constexpr result<void> parse_for_each(std::index_sequence<> /*unused...
method format_for_each (line 231) | constexpr result<void> format_for_each(std::index_sequence<N, Ns...>...
method format_for_each (line 252) | constexpr result<void> format_for_each(std::index_sequence<> /*unuse...
class formatter<T> (line 118) | class formatter<T> {
method validate (line 24) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method formatter (line 39) | constexpr formatter() noexcept
method formatter (line 43) | constexpr formatter() noexcept
method formatter (line 50) | constexpr formatter() noexcept
method set_separator (line 54) | constexpr void set_separator(std::string_view separator) noexcept {
method set_brackets (line 58) | constexpr void set_brackets(std::string_view opening_bracket, std::s...
method parse (line 63) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 78) | constexpr result<void> format(writer& out, const T& arg) const noexc...
method formatter (line 120) | constexpr formatter() : specs_{detail::sv("("), detail::sv(")"), det...
method set_separator (line 122) | constexpr void set_separator(std::string_view separator) noexcept {
method set_brackets (line 126) | constexpr void set_brackets(std::string_view opening_bracket, std::s...
method validate (line 131) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 146) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 162) | constexpr result<void> format(writer& out, const T& args) const noex...
method validate_for_each (line 171) | static constexpr result<void> validate_for_each(std::index_sequence<...
method validate_for_each (line 196) | static constexpr result<void> validate_for_each(std::index_sequence<...
method parse_for_each (line 201) | constexpr result<void> parse_for_each(std::index_sequence<Ns...> /*u...
method parse_for_each (line 221) | constexpr result<void> parse_for_each(std::index_sequence<> /*unused...
method format_for_each (line 231) | constexpr result<void> format_for_each(std::index_sequence<N, Ns...>...
method format_for_each (line 252) | constexpr result<void> format_for_each(std::index_sequence<> /*unuse...
FILE: include/emio/reader.hpp
type emio (line 17) | namespace emio {
class reader (line 20) | class reader
method reader (line 50) | constexpr reader() = default;
method reader (line 53) | constexpr reader(std::nullptr_t) = delete;
method reader (line 54) | constexpr reader(int) = delete;
method reader (line 56) | constexpr reader(std::string&&) = delete;
method reader (line 66) | constexpr explicit reader(Arg&& input) noexcept : reader{std::string...
method reader (line 72) | constexpr explicit reader(const std::string_view& sv) noexcept
method pos (line 79) | [[nodiscard]] constexpr size_t pos() const noexcept {
method eof (line 87) | [[nodiscard]] constexpr bool eof() const noexcept {
method cnt_remaining (line 95) | [[nodiscard]] constexpr size_t cnt_remaining() const noexcept {
method view_remaining (line 103) | [[nodiscard]] constexpr std::string_view view_remaining() const noex...
method read_remaining (line 111) | [[nodiscard]] constexpr std::string_view read_remaining() noexcept {
method pop (line 122) | constexpr void pop(const size_t cnt = 1) noexcept {
method unpop (line 135) | constexpr void unpop(const size_t cnt = 1) noexcept {
method subreader (line 150) | constexpr result<reader> subreader(const size_t pos, const size_t le...
method peek (line 163) | constexpr result<char> peek() const noexcept {
method read_char (line 174) | constexpr result<char> read_char() noexcept {
method read_n_chars (line 186) | constexpr result<std::string_view> read_n_chars(const size_t n) noex...
method parse_int (line 204) | constexpr result<T> parse_int(const int base = 10) noexcept {
type read_until_options (line 231) | struct read_until_options {
method read_until_char (line 244) | constexpr result<std::string_view> read_until_char(
method read_until_str (line 256) | constexpr result<std::string_view> read_until_str(const std::string_...
method read_until_any_of (line 267) | constexpr result<std::string_view> read_until_any_of(
method read_until_none_of (line 279) | constexpr result<std::string_view> read_until_none_of(
method read_until (line 293) | constexpr result<std::string_view>
method read_if_match_char (line 304) | constexpr result<char> read_if_match_char(const char c) noexcept {
method read_if_match_str (line 320) | constexpr result<std::string_view> read_if_match_str(const std::stri...
method read_until_options (line 339) | [[nodiscard]] static constexpr read_until_options default_read_until...
method parse_sign_and_int (line 344) | constexpr result<T> parse_sign_and_int(const int base) noexcept {
method read_until_pos (line 349) | constexpr result<std::string_view> read_until_pos(size_t pos, const ...
method read_until_match (line 358) | constexpr result<std::string_view> read_until_match(const char* matc...
type detail (line 22) | namespace detail {
function parse_sign (line 396) | inline constexpr result<bool> parse_sign(reader& in) noexcept {
function parse_int (line 409) | constexpr result<T> parse_int(reader& in, const int base, const bool...
class reader (line 40) | class reader {
method reader (line 50) | constexpr reader() = default;
method reader (line 53) | constexpr reader(std::nullptr_t) = delete;
method reader (line 54) | constexpr reader(int) = delete;
method reader (line 56) | constexpr reader(std::string&&) = delete;
method reader (line 66) | constexpr explicit reader(Arg&& input) noexcept : reader{std::string...
method reader (line 72) | constexpr explicit reader(const std::string_view& sv) noexcept
method pos (line 79) | [[nodiscard]] constexpr size_t pos() const noexcept {
method eof (line 87) | [[nodiscard]] constexpr bool eof() const noexcept {
method cnt_remaining (line 95) | [[nodiscard]] constexpr size_t cnt_remaining() const noexcept {
method view_remaining (line 103) | [[nodiscard]] constexpr std::string_view view_remaining() const noex...
method read_remaining (line 111) | [[nodiscard]] constexpr std::string_view read_remaining() noexcept {
method pop (line 122) | constexpr void pop(const size_t cnt = 1) noexcept {
method unpop (line 135) | constexpr void unpop(const size_t cnt = 1) noexcept {
method subreader (line 150) | constexpr result<reader> subreader(const size_t pos, const size_t le...
method peek (line 163) | constexpr result<char> peek() const noexcept {
method read_char (line 174) | constexpr result<char> read_char() noexcept {
method read_n_chars (line 186) | constexpr result<std::string_view> read_n_chars(const size_t n) noex...
method parse_int (line 204) | constexpr result<T> parse_int(const int base = 10) noexcept {
type read_until_options (line 231) | struct read_until_options {
method read_until_char (line 244) | constexpr result<std::string_view> read_until_char(
method read_until_str (line 256) | constexpr result<std::string_view> read_until_str(const std::string_...
method read_until_any_of (line 267) | constexpr result<std::string_view> read_until_any_of(
method read_until_none_of (line 279) | constexpr result<std::string_view> read_until_none_of(
method read_until (line 293) | constexpr result<std::string_view>
method read_if_match_char (line 304) | constexpr result<char> read_if_match_char(const char c) noexcept {
method read_if_match_str (line 320) | constexpr result<std::string_view> read_if_match_str(const std::stri...
method read_until_options (line 339) | [[nodiscard]] static constexpr read_until_options default_read_until...
method parse_sign_and_int (line 344) | constexpr result<T> parse_sign_and_int(const int base) noexcept {
method read_until_pos (line 349) | constexpr result<std::string_view> read_until_pos(size_t pos, const ...
method read_until_match (line 358) | constexpr result<std::string_view> read_until_match(const char* matc...
type detail (line 386) | namespace detail {
function parse_sign (line 396) | inline constexpr result<bool> parse_sign(reader& in) noexcept {
function parse_int (line 409) | constexpr result<T> parse_int(reader& in, const int base, const bool...
FILE: include/emio/result.hpp
type emio (line 36) | namespace emio {
type err (line 41) | enum class err {
function to_string (line 55) | constexpr std::string_view to_string(err error) noexcept {
class bad_result_access (line 90) | class bad_result_access : public std::logic_error {
method bad_result_access (line 96) | explicit bad_result_access(const std::string_view& msg) : logic_erro...
method bad_result_access (line 105) | explicit bad_result_access(const std::string_view& msg) : msg_{msg} {}
class bad_result_access (line 99) | class bad_result_access : public std::exception {
method bad_result_access (line 96) | explicit bad_result_access(const std::string_view& msg) : logic_erro...
method bad_result_access (line 105) | explicit bad_result_access(const std::string_view& msg) : msg_{msg} {}
type detail (line 116) | namespace detail {
function throw_bad_result_access_or_terminate (line 125) | [[noreturn]] inline void throw_bad_result_access_or_terminate(const ...
type is_result (line 505) | struct is_result : std::false_type {}
type is_result<result<T>> (line 508) | struct is_result<result<T>> : std::true_type {}
class result (line 144) | class [[nodiscard]] result
class result<Value> (line 152) | class [[nodiscard]] result<Value> {
method result (line 157) | constexpr result() = delete;
method result (line 166) | constexpr explicit(!std::is_convertible_v<U, Value>) result(U&& valu...
method result (line 172) | constexpr result(err error) : error_{error} {
method has_value (line 188) | [[nodiscard]] constexpr bool has_value() const noexcept {
method has_error (line 196) | [[nodiscard]] constexpr bool has_error() const noexcept {
method Value (line 225) | [[nodiscard]] constexpr Value& operator*() & noexcept {
method Value (line 235) | [[nodiscard]] constexpr const Value& operator*() const& noexcept {
method Value (line 245) | [[nodiscard]] constexpr Value&& operator*() && noexcept {
method Value (line 255) | [[nodiscard]] constexpr const Value&& operator*() const&& noexcept {
method Value (line 265) | [[nodiscard]] constexpr Value& assume_value() & noexcept {
method Value (line 275) | [[nodiscard]] constexpr const Value& assume_value() const& noexcept {
method Value (line 285) | [[nodiscard]] constexpr Value&& assume_value() && noexcept {
method Value (line 295) | [[nodiscard]] constexpr const Value&& assume_value() const&& noexcept {
method Value (line 304) | constexpr Value& value() & noexcept(detail::exceptions_disabled) {
method Value (line 315) | [[nodiscard]] constexpr const Value& value() const& noexcept(detail:...
method Value (line 326) | constexpr Value&& value() && noexcept(detail::exceptions_disabled) {
method Value (line 338) | constexpr const Value&& value() const&& noexcept(detail::exceptions_...
method err (line 350) | [[nodiscard]] constexpr err assume_error() const noexcept {
method err (line 360) | constexpr err error() const noexcept(detail::exceptions_disabled) { ...
method Value (line 374) | constexpr Value value_or(U&& default_value) const& {
method Value (line 384) | constexpr Value value_or(U&& default_value) && {
class result<void> (line 399) | class [[nodiscard]] result<void> {
method result (line 404) | constexpr result() = delete;
method result (line 410) | constexpr result([[maybe_unused]] decltype(success) s) {}
method result (line 416) | constexpr result(err error) : error_{error} {
method result (line 426) | constexpr result(const result<T>& other) {
method has_value (line 444) | [[nodiscard]] constexpr bool has_value() const noexcept {
method has_error (line 452) | [[nodiscard]] constexpr bool has_error() const noexcept {
method assume_value (line 460) | constexpr void assume_value() const noexcept {
method value (line 469) | constexpr void value() const noexcept(detail::exceptions_disabled) {
method err (line 480) | [[nodiscard]] constexpr err assume_error() const noexcept {
method err (line 490) | constexpr err error() const noexcept(detail::exceptions_disabled) { ...
type detail (line 502) | namespace detail {
function throw_bad_result_access_or_terminate (line 125) | [[noreturn]] inline void throw_bad_result_access_or_terminate(const ...
type is_result (line 505) | struct is_result : std::false_type {}
type is_result<result<T>> (line 508) | struct is_result<result<T>> : std::true_type {}
FILE: include/emio/scan.hpp
type emio (line 11) | namespace emio {
function make_scan_args (line 39) | [[nodiscard]] detail::args_storage<detail::scan::scan_arg, sizeof...(A...
function vscan_from (line 50) | inline result<void> vscan_from(reader& in_rdr, const scan_args& args) ...
function vscan (line 60) | inline result<void> vscan(std::string_view in, const scan_args& args) ...
function scan_from (line 77) | constexpr result<void> scan_from(reader& in_rdr, format_scan_string<Ar...
function scan (line 94) | constexpr result<void> scan(std::string_view input, format_scan_string...
FILE: include/emio/scanner.hpp
type emio (line 12) | namespace emio {
class scanner (line 27) | class scanner {
method scanner (line 30) | scanner() = delete;
method validate (line 38) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 47) | constexpr result<void> parse(reader& format_rdr) noexcept {
method scan (line 57) | constexpr result<void> scan(reader& in, T& arg) const noexcept {
class scanner<T> (line 74) | class scanner<T> {
method validate (line 76) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 89) | constexpr result<void> parse(reader& format_rdr) noexcept {
method scan (line 93) | constexpr result<void> scan(reader& in, T& arg) const noexcept {
method scan (line 111) | constexpr result<void> scan(reader& in, T& arg) noexcept {
class scanner<T> (line 106) | class scanner<T> : public scanner<detail::upcasted_int_t<T>> {
method validate (line 76) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 89) | constexpr result<void> parse(reader& format_rdr) noexcept {
method scan (line 93) | constexpr result<void> scan(reader& in, T& arg) const noexcept {
method scan (line 111) | constexpr result<void> scan(reader& in, T& arg) noexcept {
class scanner<std::string_view> (line 126) | class scanner<std::string_view> {
method validate (line 128) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 135) | constexpr result<void> parse(reader& format_rdr) noexcept {
method scan (line 141) | constexpr result<void> scan(reader& in, std::string_view& arg) noexc...
class scanner<std::string> (line 155) | class scanner<std::string> : public scanner<std::string_view> {
method scan (line 157) | constexpr result<void> scan(reader& in, std::string& arg) noexcept {
FILE: include/emio/std.hpp
type emio (line 25) | namespace emio {
class formatter<std::optional<T>> (line 33) | class formatter<std::optional<T>> {
method validate (line 35) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 39) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 43) | constexpr result<void> format(writer& out, const std::optional<T>& a...
class formatter<T> (line 63) | class formatter<T> : public formatter<std::string_view> {
method format (line 65) | result<void> format(writer& out, const std::exception& arg) const no...
class formatter<std::filesystem::path> (line 75) | class formatter<std::filesystem::path> : public formatter<std::string_...
method format (line 77) | result<void> format(writer& out, const std::filesystem::path& arg) c...
class formatter<std::monostate> (line 87) | class formatter<std::monostate> {
method validate (line 89) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 93) | static constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 97) | static constexpr result<void> format(writer& out, const std::monosta...
type detail (line 102) | namespace detail {
function format_escaped_alternative (line 105) | constexpr result<void> format_escaped_alternative(writer& out, const...
class formatter<std::expected<T, E>> (line 165) | class formatter<std::expected<T, E>> {
method validate (line 167) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 171) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 175) | constexpr result<void> format(writer& out, const std::expected<T, E>...
class formatter<std::variant<Ts...>> (line 128) | class formatter<std::variant<Ts...>> {
method validate (line 130) | static constexpr result<void> validate(reader& format_rdr) noexcept {
method parse (line 134) | constexpr result<void> parse(reader& format_rdr) noexcept {
method format (line 138) | constexpr result<void> format(writer& out, const std::variant<Ts...>& ...
FILE: include/emio/writer.hpp
type emio (line 15) | namespace emio {
class writer (line 20) | class writer {
method writer (line 26) | constexpr writer(buffer& buf) noexcept : buf_{buf} {}
method buffer (line 32) | [[nodiscard]] constexpr buffer& get_buffer() noexcept {
method write_char (line 41) | constexpr result<void> write_char(const char c) noexcept {
method write_char_n (line 53) | constexpr result<void> write_char_n(const char c, const size_t n) no...
method write_char_escaped (line 69) | constexpr result<void> write_char_escaped(const char c) noexcept {
method write_str (line 79) | constexpr result<void> write_str(const std::string_view sv) noexcept {
method write_str_escaped (line 97) | constexpr result<void> write_str_escaped(const std::string_view sv) ...
type write_int_options (line 104) | struct write_int_options {
method write_int (line 117) | constexpr result<void> write_int(const T integer,
method write_int_options (line 126) | static constexpr write_int_options default_write_int_options() noexc...
method write_int_impl (line 132) | constexpr result<void> write_int_impl(const T integer, const write_i...
FILE: test/compile_test/compile.cpp
function created_with_span_buffer (line 3) | consteval auto created_with_span_buffer() {
function created_with_static_buffer (line 9) | consteval auto created_with_static_buffer() {
function created_with_memory_buffer (line 18) | consteval auto created_with_memory_buffer() {
function created_with_iterator_buffer (line 27) | consteval auto created_with_iterator_buffer() {
function main (line 35) | int main() {
FILE: test/fuzzy/dragon4/main.cpp
type Buffer (line 22) | struct Buffer {
function print_shortest_header (line 36) | void print_shortest_header(double d) {
function print_fixed_header (line 40) | void print_fixed_header(double d, int16_t precision) {
function print_exact_header (line 44) | void print_exact_header(double d, int16_t precision) {
function print_result (line 49) | void print_result(std::string_view name, std::span<T> digits, int16_t k) {
function remove_trailing_zeros (line 55) | std::span<T> remove_trailing_zeros(std::span<T> digits) {
function test_shortest (line 62) | void test_shortest(double d) {
function test_fixed (line 88) | void test_fixed(double d, int16_t precision) {
function test_exact (line 120) | void test_exact(double d, int16_t precision) {
function main (line 156) | int main() {
FILE: test/fuzzy/dragon4/rust_ref/src/lib.rs
type Buffer (line 15) | struct Buffer {
function rust_shortest (line 22) | extern "C" fn rust_shortest(d: f64) -> Buffer {
function rust_fixed (line 44) | extern "C" fn rust_fixed(d: f64, precision: i16) -> Buffer {
function rust_exponent (line 66) | extern "C" fn rust_exponent(d: f64, limit: i16) -> Buffer {
function rust_free (line 89) | extern "C" fn rust_free(buf: Buffer) {
function it_works (line 106) | fn it_works() {
FILE: test/size_test/base.cpp
function main (line 1) | int main() {}
FILE: test/size_test/emio/doFormat_a.cpp
function main (line 3) | int main() {
FILE: test/size_test/emio/format_all.cpp
function main (line 3) | int main() {
FILE: test/size_test/emio/format_all_and_extra.cpp
function main (line 3) | int main() {
FILE: test/size_test/emio/format_and_scan_all.cpp
function main (line 3) | int main(int /*c*/, char* args[]) {
FILE: test/size_test/emio/format_and_scan_all_runtime.cpp
function main (line 3) | int main(int /*c*/, char* args[]) {
FILE: test/size_test/emio/format_and_write_int.cpp
function main (line 3) | int main() {
FILE: test/size_test/emio/format_double.cpp
function main (line 3) | int main() {
FILE: test/size_test/emio/format_int.cpp
function main (line 3) | int main() {
FILE: test/size_test/emio/format_int_twice.cpp
function main (line 3) | int main() {
FILE: test/size_test/emio/format_runtime.cpp
function main (line 3) | int main() {
FILE: test/size_test/emio/format_to.cpp
function main (line 4) | int main() {
FILE: test/size_test/emio/format_to_n.cpp
function main (line 4) | int main() {
FILE: test/size_test/emio/scan_all.cpp
function main (line 3) | int main(int /*c*/, char* args[]) {
FILE: test/size_test/emio/scan_int.cpp
function main (line 3) | int main(int /*c*/, char* args[]) {
FILE: test/size_test/emio/vformat.cpp
function main (line 3) | int main() {
FILE: test/size_test/emio/write_int.cpp
function main (line 3) | int main() {
FILE: test/size_test/fmt/doFormat_a.cpp
function main (line 5) | int main() {
FILE: test/size_test/fmt/fmt_dragon.cpp
function main (line 3) | int main(int c, char*[] /*unused*/) {
FILE: test/size_test/fmt/fmt_grisu.cpp
function main (line 3) | int main(int c, char*[] /*unused*/) {
FILE: test/size_test/fmt/fmt_grisu_and_dragon.cpp
function main (line 3) | int main(int c, char*[] /*unused*/) {
FILE: test/size_test/fmt/format_all.cpp
function main (line 3) | int main() {
FILE: test/size_test/fmt/format_all_and_extra.cpp
function main (line 6) | int main() {
FILE: test/size_test/fmt/format_int.cpp
function main (line 3) | int main() {
FILE: test/size_test/fmt/format_int_twice.cpp
function main (line 3) | int main() {
FILE: test/size_test/fmt/format_runtime.cpp
function main (line 3) | int main() {
FILE: test/size_test/fmt/format_to.cpp
function main (line 5) | int main() {
FILE: test/size_test/fmt/format_to_n.cpp
function main (line 5) | int main() {
FILE: test/size_test/fmt/vformat.cpp
function main (line 3) | int main() {
FILE: test/size_test/std/locale.cpp
function main (line 3) | int main() {
FILE: test/size_test/std/snprintf.cpp
function main (line 5) | int main(int c, char* args[]) {
FILE: test/size_test/std/snprintf_and_sscanf.cpp
function main (line 5) | int main(int c, char* args[]) {
FILE: test/size_test/std/sscanf.cpp
function main (line 5) | int main(int /*c*/, char* args[]) {
FILE: test/size_test/std/string.cpp
function main (line 3) | int main() {
FILE: test/size_test/std/string_stream.cpp
function main (line 3) | int main() {
FILE: test/size_test/std/to_chars.cpp
function main (line 4) | int main() {
FILE: test/size_test/std/to_string_double.cpp
function main (line 3) | int main() {
FILE: test/size_test/std/to_string_int.cpp
function main (line 3) | int main() {
FILE: test/size_test/stubs.cpp
function _exit (line 3) | void _exit(int /*unused*/) {
function _getpid (line 8) | int _getpid() {
function _kill (line 12) | int _kill(int /*unused*/, int /*unused*/) {
function _isatty (line 20) | int _isatty(int /*unused*/) {
function _lseek (line 24) | int _lseek(int /*unused*/, int /*unused*/, int /*unused*/) {
type stat (line 28) | struct stat
function _fstat (line 30) | int _fstat(int /*unused*/, struct stat* /*st*/) {
function _read (line 34) | int _read(int /*unused*/, void* /*unused*/) {
function _write (line 38) | int _write(int /*unused*/, void* /*unused*/) {
function _close (line 42) | int _close(int /*unused*/) {
type __cxxabiv1 (line 49) | namespace __cxxabiv1 {
FILE: test/static_analysis/test_main.cpp
function compile_time_test (line 4) | consteval emio::result<void> compile_time_test() {
function test (line 10) | emio::result<void> test() {
function main (line 21) | int main() {
FILE: test/unit_test/detail/test_ct_vector.cpp
function check_equality_of_vector (line 15) | void check_equality_of_vector(T& expected, T& other) {
function check_gang_of_5 (line 42) | void check_gang_of_5(T& buf) {
FILE: test/unit_test/detail/test_dragon.cpp
function check_exact_one (line 10) | static void check_exact_one(const emf::finite_result_t& decoded, std::st...
function check_shortest (line 127) | void check_shortest(double d, std::string_view expected_str, int16_t exp...
function check_fixed (line 204) | void check_fixed(const emf::finite_result_t& finite, int16_t precision, ...
function check_exponent (line 213) | void check_exponent(const emf::finite_result_t& finite, int16_t precisio...
FILE: test/unit_test/detail/test_utf.cpp
function test_escape (line 9) | [[nodiscard]] bool test_escape(std::string_view input, std::string_view ...
FILE: test/unit_test/integer_ranges.hpp
type emio::test (line 6) | namespace emio::test {
function apply_integer_ranges (line 57) | constexpr void apply_integer_ranges(T&& func) {
FILE: test/unit_test/test_buffer.cpp
function fill (line 11) | constexpr void fill(const emio::result<std::span<char>>& area, char v) {
function check_equality_of_buffer (line 16) | constexpr bool check_equality_of_buffer(T& expected, T& other, bool data...
function check_gang_of_5 (line 79) | void check_gang_of_5(T& buf, bool data_ptr_is_different) {
class dummy_span_buffer (line 402) | class dummy_span_buffer : public emio::span_buffer {
FILE: test/unit_test/test_dynamic_format_spec.cpp
class no_copy_or_move (line 12) | class no_copy_or_move {
method no_copy_or_move (line 14) | no_copy_or_move() = default;
method no_copy_or_move (line 15) | no_copy_or_move(const no_copy_or_move&) = delete;
method no_copy_or_move (line 16) | no_copy_or_move(no_copy_or_move&&) = delete;
method no_copy_or_move (line 17) | no_copy_or_move& operator=(const no_copy_or_move&) = delete;
method no_copy_or_move (line 18) | no_copy_or_move& operator=(no_copy_or_move&&) = delete;
function format_as (line 21) | constexpr auto format_as(const no_copy_or_move& /*unused*/) {
FILE: test/unit_test/test_format.cpp
function validate_format_string (line 16) | bool validate_format_string(std::string_view str) {
function function_pointer_test (line 739) | void function_pointer_test(int, double, std::string) {}
type color (line 780) | enum color { red, green, blue }
type string_viewable (line 818) | struct string_viewable {}
type formatter<string_viewable> (line 822) | struct formatter<string_viewable> : formatter<std::string_view> {
method format (line 823) | auto format(string_viewable, format_context& ctx) -> decltype(ctx.out(...
function FMT_END_NAMESPACE (line 827) | FMT_END_NAMESPACE
type explicitly_convertible_to_std_string_view (line 834) | struct explicitly_convertible_to_std_string_view {
type fmt::formatter<explicitly_convertible_to_std_string_view> (line 841) | struct fmt::formatter<explicitly_convertible_to_std_string_view> : forma...
method format (line 842) | auto format(explicitly_convertible_to_std_string_view v, format_contex...
function TEST (line 847) | TEST(format_test, format_explicitly_convertible_to_std_string_view) {
FILE: test/unit_test/test_format_as.cpp
type foo (line 7) | namespace foo {
type bar (line 9) | enum class bar {}
function format_as (line 11) | constexpr auto format_as(const bar& w) {
FILE: test/unit_test/test_formatter.cpp
type wrap (line 9) | struct wrap {
class emio::formatter<wrap> (line 16) | class emio::formatter<wrap> : public emio::formatter<int> {
method format (line 18) | constexpr result<void> format(writer& out, const wrap& arg) const noex...
type foo (line 55) | struct foo {
class emio::formatter<foo> (line 62) | class emio::formatter<foo> {
method validate (line 64) | static constexpr result<void> validate(reader& rdr) noexcept {
method parse (line 75) | constexpr result<void> parse(reader& rdr) noexcept {
method format (line 87) | constexpr result<void> format(writer& out, const foo& arg) const noexc...
type bar (line 136) | struct bar {}
class emio::formatter<bar> (line 141) | class emio::formatter<bar> {
method validate (line 143) | static result<void> validate(reader& rdr) noexcept {
method parse (line 154) | result<void> parse(reader& rdr) noexcept {
method format (line 166) | result<void> format(writer& out, const bar& /*arg*/) const noexcept {
type foobar (line 189) | struct foobar {
class emio::formatter<foobar> (line 196) | class emio::formatter<foobar> {
method parse (line 198) | constexpr result<void> parse(reader& rdr) noexcept {
method format (line 210) | constexpr result<void> format(writer& out, const foobar& arg) const no...
type bazz0 (line 259) | struct bazz0 {}
type bazz1 (line 260) | struct bazz1 {}
type bazz2 (line 261) | struct bazz2 {}
type bazz3 (line 262) | struct bazz3 {}
class emio::formatter<bazz0> (line 267) | class emio::formatter<bazz0> {
class emio::formatter<bazz1> (line 273) | class emio::formatter<bazz1> {
class emio::formatter<bazz2> (line 279) | class emio::formatter<bazz2> {
class emio::formatter<bazz3> (line 285) | class emio::formatter<bazz3> {
type f1 (line 314) | struct f1 {}
type f2 (line 315) | struct f2 {}
type f3 (line 316) | struct f3 {}
class emio::formatter<f1> (line 320) | class emio::formatter<f1> {
class emio::formatter<f2> (line 325) | class emio::formatter<f2> {
class emio::formatter<f3> (line 331) | class emio::formatter<f3> {
FILE: test/unit_test/test_ranges.cpp
type adl (line 85) | namespace adl {
type box (line 86) | struct box {
function begin (line 90) | auto begin(const box& b) -> const int* {
function end (line 94) | auto end(const box& b) -> const int* {
type unformattable (line 124) | struct unformattable {}
type tuple_like (line 141) | struct tuple_like {
method get (line 147) | int get() const noexcept {
method get (line 152) | std::string_view get() const noexcept {
function get (line 158) | auto get(const tuple_like& t) noexcept -> decltype(t.get<N>()) {
type std (line 162) | namespace std {
type tuple_size<tuple_like> (line 164) | struct tuple_size<tuple_like> : std::integral_constant<size_t, 2> {}
type tuple_element<N, tuple_like> (line 167) | struct tuple_element<N, tuple_like> {
FILE: test/unit_test/test_scan.cpp
function validate_scan_string (line 12) | bool validate_scan_string(std::string_view str) {
FILE: test/unit_test/test_std.cpp
type unformattable (line 9) | struct unformattable {}
type throws_on_move (line 42) | struct throws_on_move {
method throws_on_move (line 43) | throws_on_move() = default;
method throws_on_move (line 44) | throws_on_move(throws_on_move&&) {
function format_as (line 49) | std::string_view format_as(const throws_on_move&) {
FILE: test/unit_test/test_writer.cpp
class buffer_from_cb (line 12) | class buffer_from_cb : public emio::buffer {
method set_cache_cb (line 14) | void set_cache_cb(std::function<std::span<char>()> cache_cb) noexcept {
method request_write_area (line 18) | emio::result<std::span<char>> request_write_area(const size_t /*used*/...
Condensed preview — 147 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (625K chars).
[
{
"path": ".clang-format",
"chars": 329,
"preview": "# Run manually to reformat a file:\n# clang-format -i --style=file <file>\nLanguage: Cpp\nBasedOnStyle: Google\nIndentPPDire"
},
{
"path": ".clang-tidy",
"chars": 6641,
"preview": "---\n# Enable ALL the things! Except not really\n# cppcoreguidelines-avoid-magic-numbers/readability-magic-numbers: covers"
},
{
"path": ".github/workflows/ci.yml",
"chars": 10096,
"preview": "name: Continuous Integration\n\non:\n push:\n branches:\n - main\n pull_request:\n\njobs:\n checks:\n runs-on: ubunt"
},
{
"path": ".github/workflows/pages.yml",
"chars": 740,
"preview": "name: Deploy Github Pages\n\non:\n push:\n branches:\n - main\n tags:\n - \"[0-9]+.[0-9]+.[0-9]+\"\n\njobs:\n depl"
},
{
"path": ".gitignore",
"chars": 123,
"preview": ".idea/\n.vs/\n.vscode/\nbuild/\ncmake/open-cpp-coverage.cmake\ncmake-build-*/\nprefix/\nCMakeLists.txt.user\nCMakeUserPresets.js"
},
{
"path": "CMakeLists.txt",
"chars": 1085,
"preview": "cmake_minimum_required(VERSION 3.14)\n\ninclude(cmake/prelude.cmake)\n\nproject(\n emio\n VERSION 0.2.0\n "
},
{
"path": "CMakePresets.json",
"chars": 5636,
"preview": "{\n \"version\": 2,\n \"cmakeMinimumRequired\": {\n \"major\": 3,\n \"minor\": 14,\n \"patch\": 0\n },\n \"configurePresets\":"
},
{
"path": "LICENSE",
"chars": 1144,
"preview": "Copyright (c) 2021 - present, Toni Neubert (viatorus/emio)\n\nCopyright (c) 2012 - present, Victor Zverovich (fmtlib/fmt)\n"
},
{
"path": "README.md",
"chars": 2763,
"preview": "\n\n[\n\nset(CMAKE_SYSTEM_NAME Linux)\nset(CMAKE_SYSTEM_VERSION 1)\nset(CMAKE_SYSTEM_PROCES"
},
{
"path": "cmake/coverage.cmake",
"chars": 913,
"preview": "# ---- Variables ----\n\n# We use variables separate from what CTest uses, because those have\n# customization issues\nset(\n"
},
{
"path": "cmake/dev-mode.cmake",
"chars": 1423,
"preview": "include(cmake/folders.cmake)\n\nset_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1)\ninclude(CTest)\n\nif (BUILD_TESTING OR B"
},
{
"path": "cmake/elf_to_size_coverage.py",
"chars": 2714,
"preview": "#!/usr/bin/env python3\n\nimport argparse\nimport re\nimport subprocess\nimport sys\nfrom io import StringIO\nfrom pathlib impo"
},
{
"path": "cmake/folders.cmake",
"chars": 893,
"preview": "set_property(GLOBAL PROPERTY USE_FOLDERS YES)\n\n# Call this function at the end of a directory scope to assign a folder t"
},
{
"path": "cmake/install-config.cmake",
"chars": 55,
"preview": "include(\"${CMAKE_CURRENT_LIST_DIR}/emioTargets.cmake\")\n"
},
{
"path": "cmake/install-rules.cmake",
"chars": 1561,
"preview": "if (PROJECT_IS_TOP_LEVEL)\n set(CMAKE_INSTALL_INCLUDEDIR include/emio CACHE PATH \"\")\nendif ()\n\n# Project is configured"
},
{
"path": "cmake/lint-targets.cmake",
"chars": 970,
"preview": "set(\n FORMAT_PATTERNS\n source/*.cpp source/*.hpp\n include/*.hpp\n test/*.cpp test/*.hpp\n "
},
{
"path": "cmake/lint.cmake",
"chars": 1437,
"preview": "cmake_minimum_required(VERSION 3.14)\n\nmacro(default name)\n if (NOT DEFINED \"${name}\")\n set(\"${name}\" \"${ARGN}\""
},
{
"path": "cmake/prelude.cmake",
"chars": 345,
"preview": "# ---- In-source guard ----\n\nif (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)\n message(\n FATAL_ERROR\n "
},
{
"path": "cmake/project-is-top-level.cmake",
"chars": 166,
"preview": "# This variable is set by project() in CMake 3.21+\nstring(\n COMPARE EQUAL\n \"${CMAKE_SOURCE_DIR}\" \"${PROJEC"
},
{
"path": "cmake/size-coverage.cmake",
"chars": 1090,
"preview": "# ---- Variables ----cd\n\nget_filename_component(COMPILER_DIR ${CMAKE_CXX_COMPILER} DIRECTORY)\n\n# We use variables separa"
},
{
"path": "cmake/variables.cmake",
"chars": 1118,
"preview": "# ---- Developer mode ----\n\n# Developer mode enables targets and code paths in the CMake scripts that are\n# only relevan"
},
{
"path": "docs/API.md",
"chars": 26343,
"preview": "# API\n\nThis is a small API overview. The public API is fully documented inside the source code. Unless otherwise stated,"
},
{
"path": "docs/CODE_OF_CONDUCT.md",
"chars": 129,
"preview": "# Code of Conduct\n\n* You will be judged by your contributions first, and your sense of humor second.\n* Nobody owes you a"
},
{
"path": "docs/CONTRIBUTING.md",
"chars": 330,
"preview": "# Contributing\n\n<!--\n Short overview, rules, general guidelines, notes about pull requests and\n style should go he"
},
{
"path": "docs/DESIGN.md",
"chars": 3703,
"preview": "# Design\n\nThis is a short design overview of *emio*.\n\n**Note:** [fmtlib/fmt](https://github.com/fmtlib/fmt) is an awesom"
},
{
"path": "docs/DEVELOPING.md",
"chars": 3076,
"preview": "# Contributing\n\nHere is some wisdom to help you build and test this project as a developer and potential contributor.\n\nT"
},
{
"path": "docs/res/class_diagram.puml",
"chars": 1573,
"preview": "@startuml\n\nclass \"scan functions\" << (F,#FF7700) >> {\n scan(input, format_str, args...) -> result<void>\n}\n\nclass \"for"
},
{
"path": "include/emio/buffer.hpp",
"chars": 23474,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/args.hpp",
"chars": 7764,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/bignum.hpp",
"chars": 10033,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/bitset.hpp",
"chars": 3044,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/conversion.hpp",
"chars": 9973,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/ct_vector.hpp",
"chars": 3754,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/format/args.hpp",
"chars": 921,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/format/decode.hpp",
"chars": 2008,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/format/dragon.hpp",
"chars": 13275,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/format/format_to.hpp",
"chars": 2843,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/format/formatter.hpp",
"chars": 29630,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/format/parser.hpp",
"chars": 2780,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/format/ranges.hpp",
"chars": 3615,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/format/specs.hpp",
"chars": 1283,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/misc.hpp",
"chars": 513,
"preview": "//\n// Copyright (c) 20213- present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/parser.hpp",
"chars": 7867,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/predef.hpp",
"chars": 2247,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/scan/args.hpp",
"chars": 1472,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/scan/parser.hpp",
"chars": 2685,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/scan/scan_from.hpp",
"chars": 1669,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/scan/scanner.hpp",
"chars": 10932,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/scan/specs.hpp",
"chars": 433,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/utf.hpp",
"chars": 4334,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/validated_string.hpp",
"chars": 4993,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/detail/validated_string_storage.hpp",
"chars": 2202,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/emio.hpp",
"chars": 1860,
"preview": "/*\n em{io} is a safe and fast high-level and low-level character input/output\n library for bare-metal and RTOS based e"
},
{
"path": "include/emio/format.hpp",
"chars": 20903,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/formatter.hpp",
"chars": 10289,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/iterator.hpp",
"chars": 3865,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/ranges.hpp",
"chars": 8043,
"preview": "//\n// Copyright (c) 2023 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/reader.hpp",
"chars": 15073,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/result.hpp",
"chars": 17145,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/scan.hpp",
"chars": 3709,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/scanner.hpp",
"chars": 4648,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/std.hpp",
"chars": 5051,
"preview": "//\n// Copyright (c) 2024 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "include/emio/writer.hpp",
"chars": 4857,
"preview": "//\n// Copyright (c) 2021 - present, Toni Neubert\n// All rights reserved.\n//\n// For the license information refer to emio"
},
{
"path": "test/benchmark/CMakeLists.txt",
"chars": 619,
"preview": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioBenchmarks LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/project-i"
},
{
"path": "test/benchmark/bench_format.cpp",
"chars": 14770,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <fmt/format.h>\n\n#include <catch2/benchmark/c"
},
{
"path": "test/benchmark/bench_scan.cpp",
"chars": 2735,
"preview": "// Unit under test.\n#include <fmt/format.h>\n\n#include <emio/scan.hpp>\n\n// Other includes.\n#include <catch2/benchmark/cat"
},
{
"path": "test/compile_test/CMakeLists.txt",
"chars": 724,
"preview": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioTests LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/project-is-top"
},
{
"path": "test/compile_test/compile.cpp",
"chars": 1281,
"preview": "#include <emio/emio.hpp>\n\nconsteval auto created_with_span_buffer() {\n std::array<char, 5> storage{};\n emio::span_buff"
},
{
"path": "test/fuzzy/dragon4/.gitignore",
"chars": 11,
"preview": "bin\noutput\n"
},
{
"path": "test/fuzzy/dragon4/Makefile",
"chars": 345,
"preview": "all: build-rust build-cpp fuzzy\n\nbuild-rust:\n\tcargo build --manifest-path rust_ref/Cargo.toml -Z unstable-options --out-"
},
{
"path": "test/fuzzy/dragon4/main.cpp",
"chars": 5259,
"preview": "#include <climits>\n\n#include \"emio/detail/format/decode.hpp\"\n#include \"emio/detail/format/dragon.hpp\"\n\nextern \"C\" {\n#inc"
},
{
"path": "test/fuzzy/dragon4/rust_ref/.gitignore",
"chars": 20,
"preview": "/target\n/Cargo.lock\n"
},
{
"path": "test/fuzzy/dragon4/rust_ref/Cargo.toml",
"chars": 177,
"preview": "[package]\nname = \"rust_ref\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[lib]\ncrate-type = [\"cdylib\"]\n\n#[unstable]\n#mtime-on-u"
},
{
"path": "test/fuzzy/dragon4/rust_ref/src/lib.rs",
"chars": 2961,
"preview": "#![feature(flt2dec)]\n#![feature(maybe_uninit_uninit_array)]\n\nextern crate core;\n\nuse core::num::flt2dec::strategy::drago"
},
{
"path": "test/fuzzy/dragon4/seeds/test1",
"chars": 22,
"preview": "123456789101112131415\n"
},
{
"path": "test/size_test/CMakeLists.txt",
"chars": 2337,
"preview": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioSizeTests LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/project-is"
},
{
"path": "test/size_test/base.cpp",
"chars": 14,
"preview": "int main() {}\n"
},
{
"path": "test/size_test/emio/doFormat_a.cpp",
"chars": 417,
"preview": "#include <emio/format.hpp>\n\nint main() {\n static_cast<void>(emio::format(\"{}\\n\", \"somefile.cpp\"));\n static_cast<void>("
},
{
"path": "test/size_test/emio/format_all.cpp",
"chars": 363,
"preview": "#include <emio/format.hpp>\n\nint main() {\n static_cast<void>(emio::format(\"{} {} {} {} {} {} {} {} {} {} {}\", true, stat"
},
{
"path": "test/size_test/emio/format_all_and_extra.cpp",
"chars": 536,
"preview": "#include <emio/emio.hpp>\n\nint main() {\n static_cast<void>(emio::format(\"{} {} {} {} {} {} {} {} {} {} {} {} {} {}\", tru"
},
{
"path": "test/size_test/emio/format_and_scan_all.cpp",
"chars": 673,
"preview": "#include <emio/emio.hpp>\n\nint main(int /*c*/, char* args[]) {\n static_cast<void>(emio::format(\"{} {} {} {} {} {} {} {} "
},
{
"path": "test/size_test/emio/format_and_scan_all_runtime.cpp",
"chars": 710,
"preview": "#include <emio/emio.hpp>\n\nint main(int /*c*/, char* args[]) {\n static_cast<void>(emio::format(emio::runtime(\"{} {} {} {"
},
{
"path": "test/size_test/emio/format_and_write_int.cpp",
"chars": 199,
"preview": "#include <emio/format.hpp>\n\nint main() {\n emio::memory_buffer buf;\n emio::writer wrt{buf};\n wrt.write_int(1).value();"
},
{
"path": "test/size_test/emio/format_double.cpp",
"chars": 89,
"preview": "#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",
"chars": 87,
"preview": "#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",
"chars": 131,
"preview": "#include <emio/format.hpp>\n\nint main() {\n static_cast<void>(emio::format(\"{}\", 1));\n static_cast<void>(emio::format(\"{"
},
{
"path": "test/size_test/emio/format_runtime.cpp",
"chars": 91,
"preview": "#include <emio/format.hpp>\n\nint main() {\n emio::format(emio::runtime(\"{}\"), 1).value();\n}\n"
},
{
"path": "test/size_test/emio/format_to.cpp",
"chars": 158,
"preview": "#include <array>\n#include <emio/format.hpp>\n\nint main() {\n std::array<char, 1> arr;\n emio::span_buffer buf{arr};\n emi"
},
{
"path": "test/size_test/emio/format_to_n.cpp",
"chars": 141,
"preview": "#include <array>\n#include <emio/format.hpp>\n\nint main() {\n std::array<char, 1> arr;\n emio::format_to_n(arr.begin(), 1,"
},
{
"path": "test/size_test/emio/scan_all.cpp",
"chars": 350,
"preview": "#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{"
},
{
"path": "test/size_test/emio/scan_int.cpp",
"chars": 126,
"preview": "#include <emio/scan.hpp>\n\nint main(int /*c*/, char* args[]) {\n int i{};\n static_cast<void>(emio::scan(args[0], \"{}\", i"
},
{
"path": "test/size_test/emio/vformat.cpp",
"chars": 101,
"preview": "#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",
"chars": 155,
"preview": "#include <emio/format.hpp>\n\nint main() {\n emio::memory_buffer buf;\n emio::writer wrt{buf};\n wrt.write_int(1).value();"
},
{
"path": "test/size_test/fmt/doFormat_a.cpp",
"chars": 425,
"preview": "#include <fmt/core.h>\n\n#include <array>\n\nint main() {\n static_cast<void>(fmt::format(\"{}\\n\", \"somefile.cpp\"));\n static"
},
{
"path": "test/size_test/fmt/fmt_dragon.cpp",
"chars": 214,
"preview": "#include \"fmt/format.h\"\n\nint main(int c, char*[] /*unused*/) {\n int exp{};\n fmt::basic_memory_buffer<char> b;\n fmt::d"
},
{
"path": "test/size_test/fmt/fmt_grisu.cpp",
"chars": 253,
"preview": "#include \"fmt/format.h\"\n\nint main(int c, char*[] /*unused*/) {\n int exp{};\n fmt::basic_memory_buffer<char> b;\n fmt::d"
},
{
"path": "test/size_test/fmt/fmt_grisu_and_dragon.cpp",
"chars": 353,
"preview": "#include \"fmt/format.h\"\n\nint main(int c, char*[] /*unused*/) {\n int exp{};\n fmt::basic_memory_buffer<char> b;\n fmt::d"
},
{
"path": "test/size_test/fmt/format_all.cpp",
"chars": 355,
"preview": "#include <fmt/core.h>\n\nint main() {\n static_cast<void>(fmt::format(\"{} {} {} {} {} {} {} {} {} {} {}\", true, static_cas"
},
{
"path": "test/size_test/fmt/format_all_and_extra.cpp",
"chars": 496,
"preview": "#include <fmt/core.h>\n#include <fmt/ranges.h>\n\n#include <array>\n\nint main() {\n static_cast<void>(fmt::format(\"{} {} {} "
},
{
"path": "test/size_test/fmt/format_int.cpp",
"chars": 81,
"preview": "#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",
"chars": 124,
"preview": "#include <fmt/core.h>\n\nint main() {\n static_cast<void>(fmt::format(\"{}\", 1));\n static_cast<void>(fmt::format(\"{}\", 2))"
},
{
"path": "test/size_test/fmt/format_runtime.cpp",
"chars": 95,
"preview": "#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",
"chars": 142,
"preview": "#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."
},
{
"path": "test/size_test/fmt/format_to_n.cpp",
"chars": 147,
"preview": "#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(ar"
},
{
"path": "test/size_test/fmt/vformat.cpp",
"chars": 105,
"preview": "#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",
"chars": 82,
"preview": "#include <locale>\n\nint main() {\n std::locale l;\n static_cast<void>(l.name());\n}\n"
},
{
"path": "test/size_test/std/snprintf.cpp",
"chars": 446,
"preview": "#include <array>\n#include <cinttypes>\n#include <cstdio>\n\nint main(int c, char* args[]) {\n std::array<char, 42> s{};\n v"
},
{
"path": "test/size_test/std/snprintf_and_sscanf.cpp",
"chars": 767,
"preview": "#include <array>\n#include <cinttypes>\n#include <cstdio>\n\nint main(int c, char* args[]) {\n std::array<char, 42> s{};\n v"
},
{
"path": "test/size_test/std/sscanf.cpp",
"chars": 440,
"preview": "#include <array>\n#include <cinttypes>\n#include <cstdio>\n\nint main(int /*c*/, char* args[]) {\n char ch{};\n int8_t i8{};"
},
{
"path": "test/size_test/std/string.cpp",
"chars": 113,
"preview": "#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",
"chars": 101,
"preview": "#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",
"chars": 150,
"preview": "#include <charconv>\n#include <string>\n\nint main() {\n std::string s;\n s.resize(42);\n std::to_chars(s.data(), s.data() "
},
{
"path": "test/size_test/std/to_string_double.cpp",
"chars": 96,
"preview": "#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",
"chars": 94,
"preview": "#include <string>\n\nint main() {\n return static_cast<int>(std::to_string(12345678).size());\n}\n"
},
{
"path": "test/size_test/stubs.cpp",
"chars": 698,
"preview": "extern \"C\" {\n\nvoid _exit(int /*unused*/) {\n while (1) {\n }\n}\n\nint _getpid() {\n return 1;\n}\n\nint _kill(int /*unused*/,"
},
{
"path": "test/static_analysis/CMakeLists.txt",
"chars": 461,
"preview": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioStaticAnalysis LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/proje"
},
{
"path": "test/static_analysis/test_main.cpp",
"chars": 573,
"preview": "// Include all.\n#include <emio/emio.hpp>\n\nconsteval emio::result<void> compile_time_test() {\n emio::static_buffer<128> "
},
{
"path": "test/unit_test/CMakeLists.txt",
"chars": 1381,
"preview": "cmake_minimum_required(VERSION 3.14)\n\nproject(emioTests LANGUAGES CXX)\n\ninclude(${CMAKE_SOURCE_DIR}/cmake/project-is-top"
},
{
"path": "test/unit_test/detail/test_bignum.cpp",
"chars": 9890,
"preview": "// Unit under test.\n#include <emio/detail/bignum.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nTEST_"
},
{
"path": "test/unit_test/detail/test_bitset.cpp",
"chars": 3192,
"preview": "// Unit under test.\n#include <emio/detail/bitset.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nTEST_"
},
{
"path": "test/unit_test/detail/test_conversion.cpp",
"chars": 2644,
"preview": "// Unit under test.\n#include <emio/detail/conversion.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#i"
},
{
"path": "test/unit_test/detail/test_ct_vector.cpp",
"chars": 8288,
"preview": "// Unit under test.\n#include <emio/detail/ct_vector.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#in"
},
{
"path": "test/unit_test/detail/test_decode.cpp",
"chars": 2444,
"preview": "// Unit under test.\n#include \"emio/detail/format/decode.hpp\"\n\n// Other includes.\n#include <limits>\n\n#include \"catch2/cat"
},
{
"path": "test/unit_test/detail/test_dragon.cpp",
"chars": 12789,
"preview": "// Unit under test.\n#include \"emio/detail/format/dragon.hpp\"\n\n// Other includes.\n#include \"catch2/catch_test_macros.hpp\""
},
{
"path": "test/unit_test/detail/test_utf.cpp",
"chars": 5965,
"preview": "// Unit under test.\n#include <emio/detail/utf.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nnamespac"
},
{
"path": "test/unit_test/integer_ranges.hpp",
"chars": 2061,
"preview": "#pragma once\n\n#include <cstdint>\n#include <tuple>\n\nnamespace emio::test {\n\ninline constexpr std::tuple integer_ranges{\n "
},
{
"path": "test/unit_test/test_buffer.cpp",
"chars": 28312,
"preview": "// Unit under test.\n#include <emio/buffer.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <cat"
},
{
"path": "test/unit_test/test_dynamic_format_spec.cpp",
"chars": 2403,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n#include <emio/ranges.hpp>\n\n// Other includes.\n#include <catch2/catch_tes"
},
{
"path": "test/unit_test/test_format.cpp",
"chars": 43768,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <cli"
},
{
"path": "test/unit_test/test_format_api.cpp",
"chars": 1049,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namesp"
},
{
"path": "test/unit_test/test_format_as.cpp",
"chars": 1076,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nnamespace fo"
},
{
"path": "test/unit_test/test_format_could_fail_api.cpp",
"chars": 3835,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namesp"
},
{
"path": "test/unit_test/test_format_emio_vs_fmt.cpp",
"chars": 2171,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <fmt/format.h>\n\n#include <catch2/catch_test_"
},
{
"path": "test/unit_test/test_format_string.cpp",
"chars": 5720,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namesp"
},
{
"path": "test/unit_test/test_format_to_api.cpp",
"chars": 6262,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namesp"
},
{
"path": "test/unit_test/test_format_to_n_api.cpp",
"chars": 4198,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namesp"
},
{
"path": "test/unit_test/test_formatted_size.cpp",
"chars": 1462,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nusing namesp"
},
{
"path": "test/unit_test/test_formatter.cpp",
"chars": 8770,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nnamespace {\n"
},
{
"path": "test/unit_test/test_iterator.cpp",
"chars": 2041,
"preview": "// Unit under test.\n#include <emio/iterator.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <s"
},
{
"path": "test/unit_test/test_print.cpp",
"chars": 1334,
"preview": "// Unit under test.\n#include <emio/format.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\nTEST_CASE(\"p"
},
{
"path": "test/unit_test/test_ranges.cpp",
"chars": 14110,
"preview": "// Formatting library for C++ - the core API\n//\n// Copyright (c) 2012 - present, Victor Zverovich\n// All rights reserved"
},
{
"path": "test/unit_test/test_reader.cpp",
"chars": 11787,
"preview": "// Unit under test.\n#include <emio/reader.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <cat"
},
{
"path": "test/unit_test/test_result.cpp",
"chars": 6123,
"preview": "// Unit under test.\n#include <emio/result.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <cat"
},
{
"path": "test/unit_test/test_scan.cpp",
"chars": 16273,
"preview": "// Unit under test.\n#include <emio/scan.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n\n#include \"inte"
},
{
"path": "test/unit_test/test_std.cpp",
"chars": 3951,
"preview": "// Unit under test.\n#include <emio/std.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <emio/f"
},
{
"path": "test/unit_test/test_writer.cpp",
"chars": 7250,
"preview": "// Unit under test.\n#include <emio/writer.hpp>\n\n// Other includes.\n#include <catch2/catch_test_macros.hpp>\n#include <fun"
}
]
About this extraction
This page contains the full source code of the Viatorus/emio GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 147 files (575.3 KB), approximately 166.9k tokens, and a symbol index with 869 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.