pax_global_header00006660000000000000000000000064147552023100014511gustar00rootroot0000000000000052 comment=680446d4cdb1f3d5ec9e46403cd19fa8586249ea glaze-4.4.3/000077500000000000000000000000001475520231000126235ustar00rootroot00000000000000glaze-4.4.3/.clang-format000066400000000000000000000073201475520231000152000ustar00rootroot00000000000000--- Language: Cpp AccessModifierOffset: -1 AlignAfterOpenBracket: Align AlignConsecutiveMacros: false AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Left AlignOperands: true AlignTrailingComments: false AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: WithoutElse AllowShortLoopsOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true BinPackParameters: true BraceWrapping: AfterCaseLabel: false AfterClass: true AfterControlStatement: false AfterEnum: false AfterFunction: true AfterNamespace: true AfterObjCDeclaration: false AfterStruct: true AfterUnion: false AfterExternBlock: false BeforeCatch: true BeforeElse: true IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 120 CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 3 ContinuationIndentWidth: 3 Cpp11BracedListStyle: true DeriveLineEnding: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: false IncludeBlocks: Regroup IncludeCategories: - Regex: ^ Priority: 2 SortPriority: 0 - Regex: ^<.*\.h> Priority: 1 SortPriority: 0 - Regex: ^<.* Priority: 2 SortPriority: 0 - Regex: .* Priority: 3 SortPriority: 0 IncludeIsMainRegex: ([-_](test|unittest))?$ IncludeIsMainSourceRegex: "" IndentCaseLabels: false IndentGotoLabels: true IndentPPDirectives: None IndentWidth: 3 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: "" MacroBlockEnd: "" MaxEmptyLinesToKeep: 1 NamespaceIndentation: All ObjCBinPackProtocolList: Never ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left ReflowComments: true SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInConditionalStatement: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false SpaceBeforeSquareBrackets: false Standard: Auto TabWidth: 8 UseCRLF: false UseTab: Never SpaceBeforeCaseColon: false BitFieldColonSpacing: Both EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: Always QualifierAlignment: Left ReferenceAlignment: Left glaze-4.4.3/.devcontainer/000077500000000000000000000000001475520231000153625ustar00rootroot00000000000000glaze-4.4.3/.devcontainer/Dockerfile000066400000000000000000000065401475520231000173610ustar00rootroot00000000000000# [Choice] bionic (18.04), focal (20.04), jammy (22.04) ARG VARIANT="jammy" FROM ubuntu:${VARIANT} # Restate the variant to use it later on in the llvm and cmake installations ARG VARIANT # Install wget, gpg, make and ninja RUN apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ apt-get install -y --no-install-recommends \ lsb-release software-properties-common \ wget apt-utils file zip \ openssh-client gnupg gpg-agent socat rsync \ make ninja-build # User-settable versions: # This Dockerfile should support gcc-[7, 8, 9, 10, 11, 12, 13] and clang-[10, 11, 12, 13. 14, 15, 16, 17] ARG GCC_VER="13" # Add gcc-${GCC_VER} RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test RUN apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ apt-get install -y --no-install-recommends \ gcc-${GCC_VER} g++-${GCC_VER} gdb # Set gcc-${GCC_VER} as default gcc RUN update-alternatives --install /usr/bin/gcc gcc $(which gcc-${GCC_VER}) 100 RUN update-alternatives --install /usr/bin/g++ g++ $(which g++-${GCC_VER}) 100 ARG LLVM_VER="17" # Add clang-${LLVM_VER} ARG LLVM_URL="http://apt.llvm.org/${VARIANT}/" ARG LLVM_PKG="llvm-toolchain-${VARIANT}-${LLVM_VER}" RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 2>/dev/null RUN add-apt-repository -y "deb ${LLVM_URL} ${LLVM_PKG} main" RUN apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ apt-get install -y --no-install-recommends \ clang-${LLVM_VER} lldb-${LLVM_VER} lld-${LLVM_VER} \ clang-tidy-${LLVM_VER} clang-format-${LLVM_VER} \ libc++-${LLVM_VER}-dev libc++abi-${LLVM_VER}-dev \ clang-tools-${LLVM_VER} libclang-common-${LLVM_VER}-dev \ libclang-${LLVM_VER}-dev libclang1-${LLVM_VER} \ clangd-${LLVM_VER} libclang-rt-${LLVM_VER}-dev \ libfuzzer-${LLVM_VER}-dev # Set clang-${LLVM_VER} as default clang RUN update-alternatives --install /usr/bin/clang clang $(which clang-${LLVM_VER}) 100 RUN update-alternatives --install /usr/bin/clang++ clang++ $(which clang++-${LLVM_VER}) 100 # Set the default clang-tidy, so CMake can find it RUN update-alternatives --install /usr/bin/clang-tidy clang-tidy $(which clang-tidy-${LLVM_VER}) 1 # Set the default clang-format RUN update-alternatives --install /usr/bin/clang-format clang-format $(which clang-format-${LLVM_VER}) 1 # Set the default clangd, so IDEs can find it RUN update-alternatives --install /usr/bin/clangd clangd $(which clangd-${LLVM_VER}) 1 # Add current cmake/ccmake, from Kitware ARG CMAKE_URL="https://apt.kitware.com/ubuntu/" ARG CMAKE_PKG=${VARIANT} RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null \ | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null RUN apt-add-repository -y "deb ${CMAKE_URL} ${CMAKE_PKG} main" RUN apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ apt-get install -y --no-install-recommends cmake cmake-curses-gui # Cleanup cached apt data we don't need anymore RUN apt-get autoremove -y && apt-get clean && \ rm -rf /var/lib/apt/lists/* # Allow the user to set compiler defaults ARG USE_CLANG # if --build-arg USE_CLANG=1, set CC to 'clang' or set to null otherwise. ENV CC=${USE_CLANG:+"clang"} ENV CXX=${USE_CLANG:+"clang++"} # if CC is null, set it to 'gcc' (or leave as is otherwise). ENV CC=${CC:-"gcc"} ENV CXX=${CXX:-"g++"} glaze-4.4.3/.devcontainer/devcontainer.json000066400000000000000000000061321475520231000207400ustar00rootroot00000000000000{ "name": "C++", "build": { "dockerfile": "Dockerfile", // Update 'VARIANT' to pick an Ubuntu OS version. Options: [bionic, focal, jammy]. Default: jammy // Update 'GCC_VER' to pick a gcc and g++ version. Options: [7, 8, 9, 10, 11, 12, 13]. Default: 13 // Update 'LLVM_VER' to pick clang version. Options: [10, 11, 12, 13, 14, 15, 16, 17]. Default: 17 // Update 'USE_CLANG' to set clang as the default C and C++ compiler. Options: [1, null]. Default null "args": { "VARIANT": "jammy", "GCC_VER": "13", "LLVM_VER": "17", "USE_CLANG": "1" } }, "features": { "ghcr.io/devcontainers/features/common-utils:2": { "username": "user", "configureZshAsDefaultShell": true }, "ghcr.io/devcontainers/features/git:1": {} }, // Needed when using a ptrace-based debugger like C++, Go, and Rust "capAdd": [ "SYS_PTRACE" ], "securityOpt": [ "seccomp=unconfined" ], // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "user", // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "id && git --version && gcc --version && clang --version && cmake --version && echo $CXX", // Configure tool-specific properties. "customizations": { // Configure properties specific to VS Code. "vscode": { // Set *default* container specific settings.json values on container create. "settings": { // Disable cpptool intellisense since we are using clangd "C_Cpp.intelliSenseEngine": "disabled", "C_Cpp.autocomplete": "disabled", "C_Cpp.errorSquiggles": "disabled", "C_Cpp.formatting": "clangFormat", // Make sure we are writing a compile_commands.json to the root for tools to use "cmake.exportCompileCommandsFile": true, // this doesn't work with CMakePresets.json use CMAKE_EXPORT_COMPILE_COMMANDS "ON" instead "cmake.copyCompileCommands": "${workspaceFolder}/compile_commands.json", "cmake.configureOnOpen": true, // cSpell Can clutter up the problems with false positives "cSpell.diagnosticLevel": "Hint", // Format only the modified code to not clutter the merge requests "editor.formatOnSave": true, "editor.formatOnSaveMode": "modifications", "terminal.integrated.shell.linux": "/bin/bash" }, // Add the IDs of extensions you want installed when the container is created. // Note that some extensions may not work in Alpine Linux. See https://aka.ms/vscode-remote/linux. "extensions": [ "ms-vscode.cpptools", "ms-vscode.cpptools-themes", "ms-vscode.cmake-tools", "twxs.cmake", "llvm-vs-code-extensions.vscode-clangd", "eamodio.gitlens", "mhutchie.git-graph", "cschlosser.doxdocgen", "streetsidesoftware.code-spell-checker", "mutantdino.resourcemonitor" ] } } } glaze-4.4.3/.editorconfig000066400000000000000000000003771475520231000153070ustar00rootroot00000000000000# EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file root = true # Settings for every file [*] end_of_line = lf insert_final_newline = true charset = utf-8 indent_style = space indent_size = 3 trim_trailing_whitespace = true glaze-4.4.3/.gitattributes000066400000000000000000000003441475520231000155170ustar00rootroot00000000000000############################### # Git Line Endings # ############################### * text=auto eol=lf *.{cmd,[cC][mM][dD]} text eol=crlf *.{bat,[bB][aA][tT]} text eol=crlf *.{vcxproj,vcxproj.filters} text eol=crlf glaze-4.4.3/.github/000077500000000000000000000000001475520231000141635ustar00rootroot00000000000000glaze-4.4.3/.github/workflows/000077500000000000000000000000001475520231000162205ustar00rootroot00000000000000glaze-4.4.3/.github/workflows/clang-format.yml000066400000000000000000000010231475520231000213110ustar00rootroot00000000000000name: clang-format on: push: branches: - main paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: DoozyX/clang-format-lint-action@v0.18.1 with: source: '.' exclude: '' extensions: 'h,hpp,cpp' clangFormatVersion: 16 style: file inplace: True - uses: EndBug/add-and-commit@v9 with: message: 'Committing clang-format changes' glaze-4.4.3/.github/workflows/clang-linux.yml000066400000000000000000000023021475520231000211610ustar00rootroot00000000000000name: clang-linux on: push: branches: - main - feature/* paths-ignore: - '**/*.md' - 'docs/**' pull_request: branches: - main paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: jobs: build: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: clang: [17, 18] build_type: [Debug] std: [23] env: CC: clang-${{matrix.clang}} CXX: clang++-${{matrix.clang}} steps: - uses: actions/checkout@v4 - name: Install dependencies run: | sudo apt-get update sudo apt-get install -y libc++-dev libc++abi-dev - name: Configure CMake run: | cmake -B ${{github.workspace}}/build \ -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_C_COMPILER=${{env.CC}} \ -DCMAKE_CXX_COMPILER=${{env.CXX}} \ -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ -DCMAKE_EXE_LINKER_FLAGS="-stdlib=libc++ -lc++abi" - name: Build run: cmake --build build -j $(nproc) - name: Test working-directory: build run: ctest -j $(nproc) --output-on-failure glaze-4.4.3/.github/workflows/clang.yml000066400000000000000000000013671475520231000200360ustar00rootroot00000000000000name: clang on: push: branches: - main - feature/* paths-ignore: - '**/*.md' - 'docs/**' pull_request: branches: - main paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: env: BUILD_TYPE: Debug jobs: build: runs-on: macos-latest steps: - uses: actions/checkout@v4 - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: latest-stable - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build run: cmake --build build --config ${{env.BUILD_TYPE}} -j 3 - name: Test working-directory: build run: ctest -C ${{env.BUILD_TYPE}} -j 3 --output-on-failure glaze-4.4.3/.github/workflows/code_coverage.yml000066400000000000000000000025131475520231000215310ustar00rootroot00000000000000name: code_coverage on: workflow_dispatch: env: BUILD_TYPE: Debug jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up environment run: | echo "CXX=clang++-16" >> "$GITHUB_ENV" echo "CC=clang-16" >> "$GITHUB_ENV" echo "CXXFLAGS="-fprofile-instr-generate -fcoverage-mapping"" >> "$GITHUB_ENV" echo "LDFLAGS="-fprofile-instr-generate"" >> "$GITHUB_ENV" echo "LLVM_PROFILE_FILE="code-%p.profraw"" >> "$GITHUB_ENV" - name: Have llvm-cov point to llvm-cov-16 run: | mkdir -p /home/runner/.local/bin ln -s `which llvm-cov-16` /home/runner/.local/bin/llvm-cov ln -s `which llvm-profdata-16` /home/runner/.local/bin/llvm-profdata - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Configure CMake with code coverage enabled run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCODE_COVERAGE=ON - name: Build code coverage target run: cmake --build build --config ${{env.BUILD_TYPE}} --target ccov-all -j 2 - name: Archive code coverage results uses: actions/upload-artifact@v3 with: name: code-coverage-report path: ${{github.workspace}}/build/ccov/all-merged glaze-4.4.3/.github/workflows/exhaustive_float.yml000066400000000000000000000020621475520231000223150ustar00rootroot00000000000000name: exhaustive float json tests on: push: branches: - main - feature/* paths-ignore: - '**/*.md' - 'docs/**' pull_request: branches: - main paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: jobs: build: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: clang: [18] build_type: [Release] std: [23] env: CC: clang-${{matrix.clang}} CXX: clang++-${{matrix.clang}} steps: - uses: actions/checkout@v4 - name: Configure CMake run: | cmake -B ${{github.workspace}}/build \ -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_C_COMPILER=${{env.CC}} \ -DCMAKE_CXX_COMPILER=${{env.CXX}} \ -DBUILD_TESTING=Off - name: Build run: cmake --build build -j $(nproc) - name: Test working-directory: build run: | set -e fuzzing/json_exhaustive_roundtrip_float echo all is OK! glaze-4.4.3/.github/workflows/exhaustive_int.yml000066400000000000000000000020631475520231000220030ustar00rootroot00000000000000name: exhaustive integer json tests on: push: branches: - main - feature/* paths-ignore: - '**/*.md' - 'docs/**' pull_request: branches: - main paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: jobs: build: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: clang: [18] build_type: [Release] std: [23] env: CC: clang-${{matrix.clang}} CXX: clang++-${{matrix.clang}} steps: - uses: actions/checkout@v4 - name: Configure CMake run: | cmake -B ${{github.workspace}}/build \ -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_C_COMPILER=${{env.CC}} \ -DCMAKE_CXX_COMPILER=${{env.CXX}} \ -DBUILD_TESTING=Off - name: Build run: cmake --build build -j $(nproc) - name: Test working-directory: build run: | set -e fuzzing/json_exhaustive_roundtrip_int echo all is OK! glaze-4.4.3/.github/workflows/gcc-x86.yml000066400000000000000000000021561475520231000201260ustar00rootroot00000000000000name: gcc-x86 on: push: branches: - main - feature/* paths-ignore: - '**/*.md' - 'docs/**' pull_request: branches: - main paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: jobs: build: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: gcc: [12, 13, 14] build_type: [Debug] std: [23] env: CC: gcc-${{matrix.gcc}} CXX: g++-${{matrix.gcc}} steps: - uses: actions/checkout@v4 - name: Setup environment run: | sudo apt update sudo apt-get install -y gcc-${{matrix.gcc}}-multilib g++-${{matrix.gcc}}-multilib libc6-dev-i386 - name: Configure CMake run: | CXXFLAGS="-g3 -m32" cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} - name: Build run: cmake --build build -j $(nproc) - name: Test run: ctest --output-on-failure --test-dir ${{github.workspace}}/build glaze-4.4.3/.github/workflows/gcc.yml000066400000000000000000000015361475520231000175040ustar00rootroot00000000000000name: gcc on: push: branches: - main - feature/* paths-ignore: - '**/*.md' - 'docs/**' pull_request: branches: - main paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: jobs: build: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: gcc: [12, 13, 14] build_type: [Debug] std: [23] env: CC: gcc-${{matrix.gcc}} CXX: g++-${{matrix.gcc}} steps: - uses: actions/checkout@v4 - name: Configure CMake run: | CXXFLAGS="-g3" cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.std}} - name: Build run: cmake --build build -j $(nproc) - name: Test run: ctest --output-on-failure --test-dir ${{github.workspace}}/build glaze-4.4.3/.github/workflows/msvc.yml000066400000000000000000000014571475520231000177220ustar00rootroot00000000000000name: msvc on: push: branches: - main - feature/* paths-ignore: - '**/*.md' - 'docs/**' pull_request: branches: - main paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: env: CTEST_OUTPUT_ON_FAILURE: 1 BUILD_TYPE: Debug jobs: build: runs-on: windows-latest timeout-minutes: 10 strategy: matrix: cpp_version: [23] steps: - uses: actions/checkout@v4 - name: Configure CMake run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_CXX_STANDARD=${{matrix.cpp_version}} - name: Build run: cmake --build build --config ${{env.BUILD_TYPE}} --parallel - name: Test working-directory: build run: ctest --build-config ${{env.BUILD_TYPE}} glaze-4.4.3/.github/workflows/msys2.yml000066400000000000000000000014751475520231000200270ustar00rootroot00000000000000name: msys2-mingw on: push: branches: - main - feature/* paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: env: BUILD_TYPE: Release CMAKE_GENERATOR: Ninja jobs: build: runs-on: windows-latest defaults: run: shell: msys2 {0} steps: - uses: actions/checkout@v4 - uses: msys2/setup-msys2@v2 with: update: true msystem: MINGW64 install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc - name: Configure CMake run: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} .. - name: Build run: cmake --build build --config ${{env.BUILD_TYPE}} -j 2 - name: Test working-directory: build run: ctest -C ${{env.BUILD_TYPE}} -j 2 --output-on-failure glaze-4.4.3/.github/workflows/quickfuzz.yml000066400000000000000000000026471475520231000210070ustar00rootroot00000000000000name: quickfuzz on: push: branches: - main - feature/* paths-ignore: - '**/*.md' - 'docs/**' pull_request: branches: - main paths-ignore: - '**/*.md' - 'docs/**' workflow_dispatch: jobs: build: runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: clang: [18] build_type: [Debug] std: [23] env: CC: clang-${{matrix.clang}} CXX: clang++-${{matrix.clang}} steps: - uses: actions/checkout@v4 - name: Install dependencies run: | sudo echo apt-get update sudo echo apt-get install -y libc++-dev libc++abi-dev - name: Configure CMake run: | cmake -B ${{github.workspace}}/build \ -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_C_COMPILER=${{env.CC}} \ -DCMAKE_CXX_COMPILER=${{env.CXX}} \ -DBUILD_TESTING=Off - name: Build run: cmake --build build -j $(nproc) - name: Test working-directory: build run: | set -e mkdir -p corpus echo number of cpus: $(nproc) echo amount of ram: free -h ../fuzzing/quickfuzz/run_all.sh echo all is OK! - uses: actions/upload-artifact@v4 if: ${{ failure() }} with: name: fuzzing-artifacts path: build/artifacts/ glaze-4.4.3/.gitignore000066400000000000000000000012121475520231000146070ustar00rootroot00000000000000# Build directories and binary files out/ **/bin/ **/build*/ **/lib*/ **/_build*/ # CMake spesific settings CMakeUserPresets.json **/CMakeFiles/ **/CMakeCache.txt # IDE files .idea/ .vs/ .vscode/ !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json *.swp *~ _ReSharper* *.log .cache/ compile_commands.json # Package Manager Files conan/ conan-cache/ conanbuildinfo.txt conaninfo.txt graph_info.json # OS Generated Files .DS_Store .AppleDouble .LSOverride ._* .Spotlight-V100 .Trashes .Trash-* $RECYCLE.BIN/ .TemporaryItems ehthumbs.db Thumbs.db *.json # Allow mock test data !tests/mock_json_test/json/*.json glaze-4.4.3/CMakeLists.txt000066400000000000000000000033651475520231000153720ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.21) include(cmake/prelude.cmake) project( glaze VERSION 4.4.3 LANGUAGES CXX ) include(cmake/project-is-top-level.cmake) include(cmake/variables.cmake) add_library(glaze_glaze INTERFACE) add_library(glaze::glaze ALIAS glaze_glaze) if (MSVC) string(REGEX MATCH "\/cl(.exe)?$" matched_cl ${CMAKE_CXX_COMPILER}) if (matched_cl) # for a C++ standards compliant preprocessor, not needed for clang-cl target_compile_options(glaze_glaze INTERFACE "/Zc:preprocessor" /permissive- /Zc:lambda) if(PROJECT_IS_TOP_LEVEL) target_compile_options(glaze_glaze INTERFACE $<$:/GL> $<$:/GL>) target_link_options(glaze_glaze INTERFACE $<$:/LTCG /INCREMENTAL:NO> $<$:/LTCG /INCREMENTAL:NO>) endif() endif() else() target_compile_options(glaze_glaze INTERFACE "-Wno-missing-braces") endif() set_property(TARGET glaze_glaze PROPERTY EXPORT_NAME glaze) target_compile_features(glaze_glaze INTERFACE cxx_std_23) target_include_directories( glaze_glaze ${warning_guard} INTERFACE "$" ) if(NOT CMAKE_SKIP_INSTALL_RULES) include(cmake/install-rules.cmake) endif() if (glaze_DEVELOPER_MODE) include(cmake/dev-mode.cmake) endif() option(glaze_DISABLE_SIMD_WHEN_SUPPORTED "disable SIMD optimizations even when targets support it (e.g. AVX2)" OFF) if(glaze_DISABLE_SIMD_WHEN_SUPPORTED) target_compile_definitions(glaze_glaze INTERFACE GLZ_DISABLE_SIMD) endif() option(glaze_BUILD_EXAMPLES "Build GLAZE examples" OFF) if(glaze_BUILD_EXAMPLES) add_subdirectory(examples) endif() glaze-4.4.3/CMakePresets.json000066400000000000000000000050041475520231000160430ustar00rootroot00000000000000{ "version": 3, "cmakeMinimumRequired": { "major": 3, "minor": 23, "patch": 0 }, "configurePresets": [ { "name": "dev", "displayName": "Developer Mode", "description": "Builds Glaze w/ tests and downloaded test dependencies", "binaryDir": "${sourceDir}/build", "generator": "Ninja", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "glaze_DEVELOPER_MODE": true, "BUILD_TESTING": true, "CMAKE_EXPORT_COMPILE_COMMANDS": true } }, { "name": "release", "displayName": "Release", "description": "Optimized library build of exclusively library", "binaryDir": "${sourceDir}/build-release", "generator": "Ninja", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release", "glaze_DEVELOPER_MODE": false, "BUILD_TESTING": false, "CMAKE_EXPORT_COMPILE_COMMANDS": true } }, { "name": "clang_latest", "displayName": "Clang 17.0.3 x86_64-pc-linux-gnu", "description": "Using compilers: C = /usr/bin/clang-17, CXX = /usr/bin/clang++-17", "binaryDir": "${sourceDir}/out/build/${presetName}", "cacheVariables": { "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", "CMAKE_C_COMPILER": "/usr/bin/clang-17", "CMAKE_CXX_COMPILER": "/usr/bin/clang++-17", "CMAKE_BUILD_TYPE": "Debug", "CMAKE_EXPORT_COMPILE_COMMANDS": true } }, { "name": "gcc", "displayName": "GCC 13.1.0 x86_64-linux-gnu", "description": "Using compilers: C = /usr/bin/gcc, CXX = /usr/bin/g++", "binaryDir": "${sourceDir}/out/build/${presetName}", "cacheVariables": { "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}", "CMAKE_C_COMPILER": "/usr/bin/gcc", "CMAKE_CXX_COMPILER": "/usr/bin/g++", "CMAKE_BUILD_TYPE": "Debug" } } ], "testPresets": [ { "name": "all", "displayName": "Automated Tests", "configurePreset": "dev", "output": { "verbosity": "verbose", "outputOnFailure": true } } ] }glaze-4.4.3/LICENSE000066400000000000000000000020701475520231000136270ustar00rootroot00000000000000MIT License Copyright (c) 2019 - present, Stephen Berry 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.glaze-4.4.3/README.md000066400000000000000000000770441475520231000141160ustar00rootroot00000000000000# Glaze One of the fastest JSON libraries in the world. Glaze reads and writes from object memory, simplifying interfaces and offering incredible performance. Glaze also supports: - [BEVE](https://github.com/beve-org/beve) (binary efficient versatile encoding) - [CSV](./docs/csv.md) (comma separated value) ## With compile time reflection for MSVC, Clang, and GCC! - Read/write aggregate initializable structs without writing any metadata or macros! - See [example on Compiler Explorer](https://gcc.godbolt.org/z/T4To5fKfz) ## Highlights - Pure, compile time reflection for structs - Powerful meta specialization system for custom names and behavior - JSON [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259) compliance with UTF-8 validation - Standard C++ library support - Header only - Direct to memory serialization/deserialization - Compile time maps with constant time lookups and perfect hashing - Powerful wrappers to modify read/write behavior ([Wrappers](./docs/wrappers.md)) - Use your own custom read/write functions ([Custom Read/Write](#custom-readwrite)) - [Handle unknown keys](./docs/unknown-keys.md) in a fast and flexible manner - Direct memory access through [JSON pointer syntax](./docs/json-pointer-syntax.md) - [JMESPath](./docs/JMESPath.md) querying - [Binary data](./docs/binary.md) through the same API for maximum performance - No exceptions (compiles with `-fno-exceptions`) - If you desire helpers that throw for cleaner syntax see [Glaze Exceptions](./docs/exceptions.md) - No runtime type information necessary (compiles with `-fno-rtti`) - Rapid error handling with short circuiting - [JSON-RPC 2.0 support](./docs/rpc/json-rpc.md) - [JSON Schema generation](./docs/json-schema.md) - Extremely portable, uses carefully optimized SWAR (SIMD Within A Register) for broad compatibility - [Partial Read](./docs/partial-read.md) and [Partial Write](./docs/partial-write.md) support - [CSV Reading/Writing](./docs/csv.md) - [Much more!](#more-features) See [DOCS](https://github.com/stephenberry/glaze/tree/main/docs) for more documentation. ## Performance | Library | Roundtrip Time (s) | Write (MB/s) | Read (MB/s) | | ------------------------------------------------------------ | ------------------ | ------------ | ----------- | | [**Glaze**](https://github.com/stephenberry/glaze) | **1.04** | **1366** | **1224** | | [**simdjson (on demand)**](https://github.com/simdjson/simdjson) | **N/A** | **N/A** | **1198** | | [**yyjson**](https://github.com/ibireme/yyjson) | **1.23** | **1005** | **1107** | | [**daw_json_link**](https://github.com/beached/daw_json_link) | **2.93** | **365** | **553** | | [**RapidJSON**](https://github.com/Tencent/rapidjson) | **3.65** | **290** | **450** | | [**Boost.JSON (direct)**](https://boost.org/libs/json) | **4.76** | **199** | **447** | | [**json_struct**](https://github.com/jorgen/json_struct) | **5.50** | **182** | **326** | | [**nlohmann**](https://github.com/nlohmann/json) | **15.71** | **84** | **80** | [Performance test code available here](https://github.com/stephenberry/json_performance) *Performance caveats: [simdjson](https://github.com/simdjson/simdjson) and [yyjson](https://github.com/ibireme/yyjson) are great, but they experience major performance losses when the data is not in the expected sequence or any keys are missing (the problem grows as the file size increases, as they must re-iterate through the document).* *Also, [simdjson](https://github.com/simdjson/simdjson) and [yyjson](https://github.com/ibireme/yyjson) do not support automatic escaped string handling, so if any of the currently non-escaped strings in this benchmark were to contain an escape, the escapes would not be handled.* [ABC Test](https://github.com/stephenberry/json_performance) shows how simdjson has poor performance when keys are not in the expected sequence: | Library | Read (MB/s) | | ------------------------------------------------------------ | ----------- | | [**Glaze**](https://github.com/stephenberry/glaze) | **678** | | [**simdjson (on demand)**](https://github.com/simdjson/simdjson) | **93** | ## Binary Performance Tagged binary specification: [BEVE](https://github.com/stephenberry/beve) | Metric | Roundtrip Time (s) | Write (MB/s) | Read (MB/s) | | --------------------- | ------------------ | ------------ | ----------- | | Raw performance | **0.42** | **3235** | **2468** | | Equivalent JSON data* | **0.42** | **3547** | **2706** | JSON size: 670 bytes BEVE size: 611 bytes *BEVE packs more efficiently than JSON, so transporting the same data is even faster. ## Examples > [!TIP] > > See the [example_json](https://github.com/stephenberry/glaze/blob/main/tests/example_json/example_json.cpp) unit test for basic examples of how to use Glaze. See [json_test](https://github.com/stephenberry/glaze/blob/main/tests/json_test/json_test.cpp) for an extensive test of features. Your struct will automatically get reflected! No metadata is required by the user. ```c++ struct my_struct { int i = 287; double d = 3.14; std::string hello = "Hello World"; std::array arr = { 1, 2, 3 }; std::map map{{"one", 1}, {"two", 2}}; }; ``` **JSON** (prettified) ```json { "i": 287, "d": 3.14, "hello": "Hello World", "arr": [ 1, 2, 3 ], "map": { "one": 1, "two": 2 } } ``` **Write JSON** ```c++ my_struct s{}; std::string buffer = glz::write_json(s).value_or("error"); ``` or ```c++ my_struct s{}; std::string buffer{}; auto ec = glz::write_json(s, buffer); if (ec) { // handle error } ``` **Read JSON** ```c++ std::string buffer = R"({"i":287,"d":3.14,"hello":"Hello World","arr":[1,2,3],"map":{"one":1,"two":2}})"; auto s = glz::read_json(buffer); if (s) // check std::expected { s.value(); // s.value() is a my_struct populated from buffer } ``` or ```c++ std::string buffer = R"({"i":287,"d":3.14,"hello":"Hello World","arr":[1,2,3],"map":{"one":1,"two":2}})"; my_struct s{}; auto ec = glz::read_json(s, buffer); // populates s from buffer if (ec) { // handle error } ``` ### Read/Write From File ```c++ auto ec = glz::read_file_json(obj, "./obj.json", std::string{}); auto ec = glz::write_file_json(obj, "./obj.json", std::string{}); ``` > [!IMPORTANT] > > The file name (2nd argument), must be null terminated. ## Compiler/System Support - Requires C++23 - Tested for both 64bit and 32bit - Only supports little-endian systems [Actions](https://github.com/stephenberry/glaze/actions) build and test with [Clang](https://clang.llvm.org) (17+), [MSVC](https://visualstudio.microsoft.com/vs/features/cplusplus/) (2022), and [GCC](https://gcc.gnu.org) (12+) on apple, windows, and linux. ![clang build](https://github.com/stephenberry/glaze/actions/workflows/clang.yml/badge.svg) ![gcc build](https://github.com/stephenberry/glaze/actions/workflows/gcc.yml/badge.svg) ![msvc build](https://github.com/stephenberry/glaze/actions/workflows/msvc.yml/badge.svg) > Glaze seeks to maintain compatibility with the latest three versions of GCC and Clang, as well as the latest version of MSVC and Apple Clang (Xcode). And, we aim to only drop old versions with major releases. ### MSVC Compiler Flags Glaze requires a C++ standard conformant pre-processor, which requires the `/Zc:preprocessor` flag when building with MSVC. ### SIMD CMake Options The CMake has the option `glaze_ENABLE_AVX2`. This will attempt to use `AVX2` SIMD instructions in some cases to improve performance, as long as the system you are configuring on supports it. Set this option to `OFF` to disable the AVX2 instruction set, such as if you are cross-compiling for Arm. If you aren't using CMake the macro `GLZ_USE_AVX2` enables the feature if defined. ## How To Use Glaze ### [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) ```cmake include(FetchContent) FetchContent_Declare( glaze GIT_REPOSITORY https://github.com/stephenberry/glaze.git GIT_TAG main GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(glaze) target_link_libraries(${PROJECT_NAME} PRIVATE glaze::glaze) ``` ### [Conan](https://conan.io) - Included in [Conan Center](https://conan.io/center/) ![Conan Center](https://img.shields.io/conan/v/glaze) ``` find_package(glaze REQUIRED) target_link_libraries(main PRIVATE glaze::glaze) ``` ### [build2](https://build2.org) - Available on [cppget](https://cppget.org/libglaze) ``` import libs = libglaze%lib{glaze} ``` ### Arch Linux - [Official Arch repository](https://archlinux.org/packages/extra/any/glaze/) - AUR git package: [glaze-git](https://aur.archlinux.org/packages/glaze-git) ### See this [Example Repository](https://github.com/stephenberry/glaze_example) for how to use Glaze in a new project --- ## See [FAQ](./docs/FAQ.md) for Frequently Asked Questions # Explicit Metadata If you want to specialize your reflection then you can **optionally** write the code below: > This metadata is also necessary for non-aggregate initializable structs. ```c++ template <> struct glz::meta { using T = my_struct; static constexpr auto value = object( &T::i, &T::d, &T::hello, &T::arr, &T::map ); }; ``` ## Local Glaze Meta
Glaze also supports metadata within its associated class: ```c++ struct my_struct { int i = 287; double d = 3.14; std::string hello = "Hello World"; std::array arr = { 1, 2, 3 }; std::map map{{"one", 1}, {"two", 2}}; struct glaze { using T = my_struct; static constexpr auto value = glz::object( &T::i, &T::d, &T::hello, &T::arr, &T::map ); }; }; ```
## Custom Key Names or Unnamed Types When you define Glaze metadata, objects will automatically reflect the non-static names of your member object pointers. However, if you want custom names or you register lambda functions or wrappers that do not provide names for your fields, you can optionally add field names in your metadata. Example of custom names: ```c++ template <> struct glz::meta { using T = my_struct; static constexpr auto value = object( "integer", &T::i, "double", &T::d, "string", &T::hello, "array", &T::arr, "my map", &T::map ); }; ``` > Each of these strings is optional and can be removed for individual fields if you want the name to be reflected. > > Names are required for: > > - static constexpr member variables > - [Wrappers](./docs/wrappers.md) > - Lambda functions # Reflection API Glaze provides a compile time reflection API that can be modified via `glz::meta` specializations. This reflection API uses pure reflection unless a `glz::meta` specialization is provided, in which case the default behavior is overridden by the developer. ```c++ static_assert(glz::reflect::size == 5); // Number of fields static_assert(glz::reflect::keys[0] == "i"); // Access keys ``` > [!WARNING] > > The `glz::reflect` fields described above have been formalized and are unlikely to change. Other fields may evolve as we continue to formalize the spec. ## glz::for_each_field ```c++ struct test_type { int32_t int1{}; int64_t int2{}; }; test_type var{42, 43}; glz::for_each_field(var, [](auto& field) { field += 1; }); expect(var.int1 == 43); expect(var.int2 == 44); ``` # Custom Read/Write Custom reading and writing can be achieved through the powerful `to`/`from` specialization approach, which is described here: [custom-serialization.md](https://github.com/stephenberry/glaze/blob/main/docs/custom-serialization.md). However, this only works for user defined types. For common use cases or cases where a specific member variable should have special reading and writing, you can use [glz::custom](https://github.com/stephenberry/glaze/blob/main/docs/wrappers.md#custom) to register read/write member functions, std::functions, or lambda functions.
See example: ```c++ struct custom_encoding { uint64_t x{}; std::string y{}; std::array z{}; void read_x(const std::string& s) { x = std::stoi(s); } uint64_t write_x() { return x; } void read_y(const std::string& s) { y = "hello" + s; } auto& write_z() { z[0] = 5; return z; } }; template <> struct glz::meta { using T = custom_encoding; static constexpr auto value = object("x", custom<&T::read_x, &T::write_x>, // "y", custom<&T::read_y, &T::y>, // "z", custom<&T::z, &T::write_z>); }; suite custom_encoding_test = [] { "custom_reading"_test = [] { custom_encoding obj{}; std::string s = R"({"x":"3","y":"world","z":[1,2,3]})"; expect(!glz::read_json(obj, s)); expect(obj.x == 3); expect(obj.y == "helloworld"); expect(obj.z == std::array{1, 2, 3}); }; "custom_writing"_test = [] { custom_encoding obj{}; std::string s = R"({"x":"3","y":"world","z":[1,2,3]})"; expect(!glz::read_json(obj, s)); std::string out{}; expect(not glz::write_json(obj, out)); expect(out == R"({"x":3,"y":"helloworld","z":[5,2,3]})"); }; }; ```
Another example with constexpr lambdas: ```c++ struct custom_buffer_input { std::string str{}; }; template <> struct glz::meta { static constexpr auto read_x = [](custom_buffer_input& s, const std::string& input) { s.str = input; }; static constexpr auto write_x = [](auto& s) -> auto& { return s.str; }; static constexpr auto value = glz::object("str", glz::custom); }; suite custom_lambdas_test = [] { "custom_buffer_input"_test = [] { std::string s = R"({"str":"Hello!"})"; custom_buffer_input obj{}; expect(!glz::read_json(obj, s)); expect(obj.str == "Hello!"); s.clear(); expect(!glz::write_json(obj, s)); expect(s == R"({"str":"Hello!"})"); expect(obj.str == "Hello!"); }; }; ```
# Object Mapping When using member pointers (e.g. `&T::a`) the C++ class structures must match the JSON interface. It may be desirable to map C++ classes with differing layouts to the same object interface. This is accomplished through registering lambda functions instead of member pointers. ```c++ template <> struct glz::meta { static constexpr auto value = object( "i", [](auto&& self) -> auto& { return self.subclass.i; } ); }; ``` The value `self` passed to the lambda function will be a `Thing` object, and the lambda function allows us to make the subclass invisible to the object interface. Lambda functions by default copy returns, therefore the `auto&` return type is typically required in order for glaze to write to memory. > Note that remapping can also be achieved through pointers/references, as glaze treats values, pointers, and references in the same manner when writing/reading. # Value Types A class can be treated as an underlying value as follows: ```c++ struct S { int x{}; }; template <> struct glz::meta { static constexpr auto value{ &S::x }; }; ``` or using a lambda: ```c++ template <> struct glz::meta { static constexpr auto value = [](auto& self) -> auto& { return self.x; }; }; ``` # Error Handling Glaze is safe to use with untrusted messages. Errors are returned as error codes, typically within a `glz::expected`, which behaves just like a `std::expected`. > Glaze works to short circuit error handling, which means the parsing exits very rapidly if an error is encountered. To generate more helpful error messages, call `format_error`: ```c++ auto pe = glz::read_json(obj, buffer); if (pe) { std::string descriptive_error = glz::format_error(pe, buffer); } ``` This test case: ```json {"Hello":"World"x, "color": "red"} ``` Produces this error: ``` 1:17: expected_comma {"Hello":"World"x, "color": "red"} ^ ``` Denoting that x is invalid here. # Input Buffer (Null) Termination A non-const `std::string` is recommended for input buffers, as this allows Glaze to improve performance with temporary padding and the buffer will be null terminated. ## JSON By default the option `null_terminated` is set to `true` and null-terminated buffers must be used when parsing JSON. The option can be turned off with a small loss in performance, which allows non-null terminated buffers: ```c++ constexpr glz::opts options{.null_terminated = false}; auto ec = glz::read(value, buffer); // read in a non-null terminated buffer ``` ## BEVE Null-termination is not required when parsing BEVE (binary). It makes no difference in performance. ## CSV > [!WARNING] > > Currently, `null_terminated = false` is not valid for CSV parsing and buffers must be null terminated. # Type Support ## Array Types Array types logically convert to JSON array values. Concepts are used to allow various containers and even user containers if they match standard library interfaces. - `glz::array` (compile time mixed types) - `std::tuple` (compile time mixed types) - `std::array` - `std::vector` - `std::deque` - `std::list` - `std::forward_list` - `std::span` - `std::set` - `std::unordered_set` ## Object Types Object types logically convert to JSON object values, such as maps. Like JSON, Glaze treats object definitions as unordered maps. Therefore the order of an object layout does not have to match the same binary sequence in C++. - `glz::object` (compile time mixed types) - `std::map` - `std::unordered_map` - `std::pair` (enables dynamic keys in stack storage) > `std::pair` is handled as an object with a single key and value, but when `std::pair` is used in an array, Glaze concatenates the pairs into a single object. `std::vector>` will serialize as a single object. If you don't want this behavior set the compile time option `.concatenate = false`. ## Variants - `std::variant` See [Variant Handling](./docs/variant-handling.md) for more information. ## Nullable Types - `std::unique_ptr` - `std::shared_ptr` - `std::optional` Nullable types may be allocated by valid input or nullified by the `null` keyword. ```c++ std::unique_ptr ptr{}; std::string buffer{}; expect(not glz::write_json(ptr, buffer)); expect(buffer == "null"); expect(not glz::read_json(ptr, "5")); expect(*ptr == 5); buffer.clear(); expect(not glz::write_json(ptr, buffer)); expect(buffer == "5"); expect(not glz::read_json(ptr, "null")); expect(!bool(ptr)); ``` ## Enums By default enums will be written and read in integer form. No `glz::meta` is necessary if this is the desired behavior. However, if you prefer to use enums as strings in JSON, they can be registered in the `glz::meta` as follows: ```c++ enum class Color { Red, Green, Blue }; template <> struct glz::meta { using enum Color; static constexpr auto value = enumerate(Red, Green, Blue ); }; ``` In use: ```c++ Color color = Color::Red; std::string buffer{}; glz::write_json(color, buffer); expect(buffer == "\"Red\""); ``` # JSON With Comments (JSONC) Comments are supported with the specification defined here: [JSONC](https://github.com/stephenberry/JSONC) Read support for comments is provided with `glz::read_jsonc` or `glz::read(...)`. # Prettify JSON Formatted JSON can be written out directly via a compile time option: ```c++ auto ec = glz::write(obj, buffer); ``` Or, JSON text can be formatted with the `glz::prettify_json` function: ```c++ std::string buffer = R"({"i":287,"d":3.14,"hello":"Hello World","arr":[1,2,3]})"); auto beautiful = glz::prettify_json(buffer); ``` `beautiful` is now: ```json { "i": 287, "d": 3.14, "hello": "Hello World", "arr": [ 1, 2, 3 ] } ``` # Minify JSON To write minified JSON: ```c++ auto ec = glz::write_json(obj, buffer); // default is minified ``` To minify JSON text call: ```c++ std::string minified = glz::minify_json(buffer); ``` ## Minified JSON Reading If you wish require minified JSON or know your input will always be minified, then you can gain a little more performance by using the compile time option `.minified = true`. ```c++ auto ec = glz::read(obj, buffer); ``` ## Boolean Flags Glaze supports registering a set of boolean flags that behave as an array of string options: ```c++ struct flags_t { bool x{ true }; bool y{}; bool z{ true }; }; template <> struct glz::meta { using T = flags_t; static constexpr auto value = flags("x", &T::x, "y", &T::y, "z", &T::z); }; ``` Example: ```c++ flags_t s{}; expect(glz::write_json(s) == R"(["x","z"])"); ``` Only `"x"` and `"z"` are written out, because they are true. Reading in the buffer will set the appropriate booleans. > When writing BEVE, `flags` only use one bit per boolean (byte aligned). ## Logging JSON Sometimes you just want to write out JSON structures on the fly as efficiently as possible. Glaze provides tuple-like structures that allow you to stack allocate structures to write out JSON with high speed. These structures are named `glz::obj` for objects and `glz::arr` for arrays. Below is an example of building an object, which also contains an array, and writing it out. ```c++ auto obj = glz::obj{"pi", 3.14, "happy", true, "name", "Stephen", "arr", glz::arr{"Hello", "World", 2}}; std::string s{}; expect(not glz::write_json(obj, s)); expect(s == R"({"pi":3.14,"happy":true,"name":"Stephen","arr":["Hello","World",2]})"); ``` > This approach is significantly faster than `glz::json_t` for generic JSON. But, may not be suitable for all contexts. ## Merge `glz::merge` allows the user to merge multiple JSON object types into a single object. ```c++ glz::obj o{"pi", 3.141}; std::map map = {{"a", 1}, {"b", 2}, {"c", 3}}; auto merged = glz::merge{o, map}; std::string s{}; glz::write_json(merged, s); // will write out a single, merged object // s is now: {"pi":3.141,"a":0,"b":2,"c":3} ``` > `glz::merge` stores references to lvalues to avoid copies ## Generic JSON See [Generic JSON](./docs/generic-json.md) for `glz::json_t`. ```c++ glz::json_t json{}; std::string buffer = R"([5,"Hello World",{"pi":3.14}])"; glz::read_json(json, buffer); assert(json[2]["pi"].get() == 3.14); ``` ## Raw Buffer Performance Glaze is just about as fast writing to a `std::string` as it is writing to a raw char buffer. If you have sufficiently allocated space in your buffer you can write to the raw buffer, as shown below, but it is not recommended. ``` glz::read_json(obj, buffer); const auto n = glz::write_json(obj, buffer.data()).value_or(0); buffer.resize(n); ``` ## Compile Time Options The `glz::opts` struct defines compile time optional settings for reading/writing. Instead of calling `glz::read_json(...)`, you can call `glz::read(...)` and customize the options. For example: `glz::read(...)` will turn off erroring on unknown keys and simple skip the items. `glz::opts` can also switch between formats: - `glz::read(...)` -> `glz::read_beve(...)` - `glz::read(...)` -> `glz::read_json(...)` ## Available Compile Time Options The struct below shows the available options and the default behavior. ```c++ struct opts { uint32_t format = json; bool comments = false; // Support reading in JSONC style comments bool error_on_unknown_keys = true; // Error when an unknown key is encountered bool skip_null_members = true; // Skip writing out params in an object if the value is null bool use_hash_comparison = true; // Will replace some string equality checks with hash checks bool prettify = false; // Write out prettified JSON bool minified = false; // Require minified input for JSON, which results in faster read performance char indentation_char = ' '; // Prettified JSON indentation char uint8_t indentation_width = 3; // Prettified JSON indentation size bool new_lines_in_arrays = true; // Whether prettified arrays should have new lines for each element bool shrink_to_fit = false; // Shrinks dynamic containers to new size to save memory bool write_type_info = true; // Write type info for meta objects in variants bool error_on_missing_keys = false; // Require all non nullable keys to be present in the object. Use // skip_null_members = false to require nullable members bool error_on_const_read = false; // Error if attempt is made to read into a const value, by default the value is skipped without error bool validate_skipped = false; // If full validation should be performed on skipped values bool validate_trailing_whitespace = false; // If, after parsing a value, we want to validate the trailing whitespace uint8_t layout = rowwise; // CSV row wise output/input // The maximum precision type used for writing floats, higher precision floats will be cast down to this precision float_precision float_max_write_precision{}; bool bools_as_numbers = false; // Read and write booleans with 1's and 0's bool quoted_num = false; // treat numbers as quoted or array-like types as having quoted numbers bool number = false; // read numbers as strings and write these string as numbers bool raw = false; // write out string like values without quotes bool raw_string = false; // do not decode/encode escaped characters for strings (improves read/write performance) bool structs_as_arrays = false; // Handle structs (reading/writing) without keys, which applies bool allow_conversions = true; // Whether conversions between convertible types are // allowed in binary, e.g. double -> float bool partial_read = false; // Reads into the deepest structural object and then exits without parsing the rest of the input // glaze_object_t concepts bool concatenate = true; // Concatenates ranges of std::pair into single objects when writing bool hide_non_invocable = true; // Hides non-invocable members from the cli_menu (may be applied elsewhere in the future) }; ``` > Many of these compile time options have wrappers to apply the option to only a single field. See [Wrappers](./docs/wrappers.md) for more details. ## JSON Conformance By default Glaze is strictly conformant with the latest JSON standard except in two cases with associated options: - `validate_skipped` This option does full JSON validation for skipped values when parsing. This is not set by default because values are typically skipped when the user is unconcerned with them, and Glaze still validates for major issues. But, this makes skipping faster by not caring if the skipped values are exactly JSON conformant. For example, by default Glaze will ensure skipped numbers have all valid numerical characters, but it will not validate for issues like leading zeros in skipped numbers unless `validate_skipped` is on. Wherever Glaze parses a value to be used it is fully validated. - `validate_trailing_whitespace` This option validates the trailing whitespace in a parsed document. Because Glaze parses C++ structs, there is typically no need to continue parsing after the object of interest has been read. Turn on this option if you want to ensure that the rest of the document has valid whitespace, otherwise Glaze will just ignore the content after the content of interest has been parsed. > [!NOTE] > > Glaze does not automatically unicode escape control characters (e.g. `"\x1f"` to `"\u001f"`), as this poses a risk of embedding null characters and other invisible characters in strings. A compile time option will be added to enable these conversions (open issue: [unicode escaped write](https://github.com/stephenberry/glaze/issues/812)), but it will not be the default behavior. ## Skip It can be useful to acknowledge a keys existence in an object to prevent errors, and yet the value may not be needed or exist in C++. These cases are handled by registering a `glz::skip` type with the meta data.
See example: ```c++ struct S { int i{}; }; template <> struct glz::meta { static constexpr auto value = object("key_to_skip", skip{}, &S::i); }; ``` ```c++ std::string buffer = R"({"key_to_skip": [1,2,3], "i": 7})"; S s{}; glz::read_json(s, buffer); // The value [1,2,3] will be skipped expect(s.i == 7); // only the value i will be read into ```
## Hide Glaze is designed to help with building generic APIs. Sometimes a value needs to be exposed to the API, but it is not desirable to read in or write out the value in JSON. This is the use case for `glz::hide`. `glz::hide` hides the value from JSON output while still allowing API (and JSON pointer) access.
See example: ```c++ struct hide_struct { int i = 287; double d = 3.14; std::string hello = "Hello World"; }; template <> struct glz::meta { using T = hide_struct; static constexpr auto value = object(&T::i, // &T::d, // "hello", hide{&T::hello}); }; ``` ```c++ hide_struct s{}; auto b = glz::write_json(s); expect(b == R"({"i":287,"d":3.14})"); // notice that "hello" is hidden from the output ```
## Quoted Numbers You can parse quoted JSON numbers directly to types like `double`, `int`, etc. by utilizing the `glz::quoted` wrapper. ```c++ struct A { double x; std::vector y; }; template <> struct glz::meta { static constexpr auto value = object("x", glz::quoted_num<&A::x>, "y", glz::quoted_num<&A::y>; }; ``` ```json { "x": "3.14", "y": ["1", "2", "3"] } ``` The quoted JSON numbers will be parsed directly into the `double` and `std::vector`. The `glz::quoted` function works for nested objects and arrays as well. ## JSON Lines (NDJSON) Support Glaze supports [JSON Lines](https://jsonlines.org) (or Newline Delimited JSON) for array-like types (e.g. `std::vector` and `std::tuple`). ```c++ std::vector x = { "Hello", "World", "Ice", "Cream" }; std::string s = glz::write_ndjson(x).value_or("error"); auto ec = glz::read_ndjson(x, s); ``` # More Features ### [Data Recorder](./docs/recorder.md) ### [Command Line Interface Menu](./docs/cli-menu.md) ### [JMESPath](./docs/JMESPath.md) - Querying JSON ### [JSON Include System](./docs/json-include.md) ### [JSON Pointer Syntax](./docs/json-pointer-syntax.md) ### [JSON-RPC 2.0](./docs/rpc/json-rpc.md) ### [JSON Schema](./docs/json-schema.md) ### [Shared Library API](./docs/glaze-interfaces.md) ### [Tagged Binary Messages](./docs/binary.md) ### [Thread Pool](./docs/thread-pool.md) ### [Time Trace Profiling](./docs/time-trace.md) - Output performance profiles to JSON and visualize using [Perfetto](https://ui.perfetto.dev) ### [Wrappers](./docs/wrappers.md) # Extensions See the `ext` directory for extensions. - [Eigen](https://gitlab.com/libeigen/eigen) - [JSON-RPC 2.0](./docs/rpc/json-rpc.md) - [Command Line Interface Menu (cli_menu)](./docs/cli-menu.md) # License Glaze is distributed under the MIT license with an exception for embedded forms: > --- Optional exception to the license --- > > As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into a machine-executable object form of such source code, you may redistribute such embedded portions in such object form without including the copyright and permission notices. glaze-4.4.3/cmake/000077500000000000000000000000001475520231000137035ustar00rootroot00000000000000glaze-4.4.3/cmake/code-coverage.cmake000066400000000000000000000672451475520231000174260ustar00rootroot00000000000000# # Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. You may obtain a copy of # the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. # USAGE: To enable any code coverage instrumentation/targets, the single CMake # option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or # on the command line. # # From this point, there are two primary methods for adding instrumentation to # targets: 1 - A blanket instrumentation by calling `add_code_coverage()`, where # all targets in that directory and all subdirectories are automatically # instrumented. 2 - Per-target instrumentation by calling # `target_code_coverage()`, where the target is given and thus only # that target is instrumented. This applies to both libraries and executables. # # To add coverage targets, such as calling `make ccov` to generate the actual # coverage information for perusal or consumption, call # `target_code_coverage()` on an *executable* target. # # Example 1: All targets instrumented # # In this case, the coverage information reported will will be that of the # `theLib` library target and `theExe` executable. # # 1a: Via global command # # ~~~ # add_code_coverage() # Adds instrumentation to all targets # # add_library(theLib lib.cpp) # # add_executable(theExe main.cpp) # target_link_libraries(theExe PRIVATE theLib) # target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target (instrumentation already added via global anyways) for generating code coverage reports. # ~~~ # # 1b: Via target commands # # ~~~ # add_library(theLib lib.cpp) # target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets. # # add_executable(theExe main.cpp) # target_link_libraries(theExe PRIVATE theLib) # target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports. # ~~~ # # Example 2: Target instrumented, but with regex pattern of files to be excluded # from report # # ~~~ # add_executable(theExe main.cpp non_covered.cpp) # target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder. # ~~~ # # Example 3: Target added to the 'ccov' and 'ccov-all' targets # # ~~~ # add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders. # # add_executable(theExe main.cpp non_covered.cpp) # target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder. # ~~~ # Options option( CODE_COVERAGE "Builds targets with code coverage instrumentation. (Requires GCC or Clang)" OFF) # Programs find_program(LLVM_COV_PATH llvm-cov) find_program(LLVM_PROFDATA_PATH llvm-profdata) find_program(LCOV_PATH lcov) find_program(GENHTML_PATH genhtml) # Hide behind the 'advanced' mode flag for GUI/ccmake mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH) # Variables set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov) set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1) # Common initialization/checks if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED) set(CODE_COVERAGE_ADDED ON) # Common Targets file(MAKE_DIRECTORY ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}) if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") # Messages message(STATUS "Building with llvm Code Coverage Tools") if(NOT LLVM_COV_PATH) message(FATAL_ERROR "llvm-cov not found! Aborting.") else() # Version number checking for 'EXCLUDE' compatibility execute_process(COMMAND ${LLVM_COV_PATH} --version OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT) string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION ${LLVM_COV_VERSION_CALL_OUTPUT}) if(LLVM_COV_VERSION VERSION_LESS "7.0.0") message( WARNING "target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0" ) endif() endif() # Targets if(${CMAKE_VERSION} VERSION_LESS "3.17.0") add_custom_target( ccov-clean COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) else() add_custom_target( ccov-clean COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list COMMAND ${CMAKE_COMMAND} -E rm -f ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) endif() # Used to get the shared object file list before doing the main all- # processing add_custom_target( ccov-libs COMMAND ; COMMENT "libs ready for coverage report.") elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") # Messages message(STATUS "Building with lcov Code Coverage Tools") if(CMAKE_BUILD_TYPE) string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type) if(NOT ${upper_build_type} STREQUAL "DEBUG") message( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" ) endif() else() message( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" ) endif() if(NOT LCOV_PATH) message(FATAL_ERROR "lcov not found! Aborting...") endif() if(NOT GENHTML_PATH) message(FATAL_ERROR "genhtml not found! Aborting...") endif() # Targets add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters) else() message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.") endif() endif() # Adds code coverage instrumentation to a library, or instrumentation/targets # for an executable target. # ~~~ # EXECUTABLE ADDED TARGETS: # GCOV/LCOV: # ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. # ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target. # ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. # # LLVM-COV: # ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. # ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter. # ccov-${TARGET_NAME} : Generates HTML code coverage report. # ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information. # ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file. # ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information. # ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. # ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line. # ccov-all-export : Exports the coverage report to a JSON file. # # Required: # TARGET_NAME - Name of the target to generate code coverage for. # Optional: # PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE. # INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE. # PLAIN - Do not set any target visibility (backward compatibility with old cmake projects) # AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets. # ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets. # EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory # COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`. # EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.** # OBJECTS - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output # ARGS - For executables ONLY, appends the given arguments to the associated ccov-* executable call # ~~~ function(target_code_coverage TARGET_NAME) # Argument parsing set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN) set(single_value_keywords COVERAGE_TARGET_NAME) set(multi_value_keywords EXCLUDE OBJECTS ARGS) cmake_parse_arguments( target_code_coverage "${options}" "${single_value_keywords}" "${multi_value_keywords}" ${ARGN}) # Set the visibility of target functions to PUBLIC, INTERFACE or default to # PRIVATE. if(target_code_coverage_PUBLIC) set(TARGET_VISIBILITY PUBLIC) set(TARGET_LINK_VISIBILITY PUBLIC) elseif(target_code_coverage_INTERFACE) set(TARGET_VISIBILITY INTERFACE) set(TARGET_LINK_VISIBILITY INTERFACE) elseif(target_code_coverage_PLAIN) set(TARGET_VISIBILITY PUBLIC) set(TARGET_LINK_VISIBILITY) else() set(TARGET_VISIBILITY PRIVATE) set(TARGET_LINK_VISIBILITY PRIVATE) endif() if(NOT target_code_coverage_COVERAGE_TARGET_NAME) # If a specific name was given, use that instead. set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME}) endif() if(CODE_COVERAGE) # Add code coverage instrumentation to the target's linker command if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-instr-generate -fcoverage-mapping) target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-instr-generate -fcoverage-mapping) elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") target_compile_options( ${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs -ftest-coverage $<$:-fno-elide-constructors> -fno-default-inline) target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov) endif() # Targets get_target_property(target_type ${TARGET_NAME} TYPE) # Add shared library to processing for 'all' targets if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL) if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") add_custom_target( ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${CMAKE_COMMAND} -E echo "-object=$" >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list DEPENDS ${TARGET_NAME}) if(NOT TARGET ccov-libs) message( FATAL_ERROR "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." ) endif() add_dependencies(ccov-libs ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) endif() endif() # For executables add targets to run and produce output if(target_type STREQUAL "EXECUTABLE") if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") # If there are shared objects to also work with, generate the string to # add them here foreach(SO_TARGET ${target_code_coverage_OBJECTS}) # Check to see if the target is a shared object if(TARGET ${SO_TARGET}) get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE) if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY") set(SO_OBJECTS ${SO_OBJECTS} -object=$) endif() endif() endforeach() # Run the executable, generating raw profile data Make the run data # available for further processing. Separated to allow Windows to run # this target serially. add_custom_target( ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${CMAKE_COMMAND} -E env LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw $ ${target_code_coverage_ARGS} COMMAND ${CMAKE_COMMAND} -E echo "-object=$" ${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list COMMAND ${CMAKE_COMMAND} -E echo "${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw" >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list JOB_POOL ccov_serial_pool DEPENDS ccov-libs ${TARGET_NAME}) # Merge the generated profile data so llvm-cov can process it add_custom_target( ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${LLVM_PROFDATA_PATH} merge -sparse ${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o ${target_code_coverage_COVERAGE_TARGET_NAME}.profdata DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) # Ignore regex only works on LLVM >= 7 if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) set(EXCLUDE_REGEX ${EXCLUDE_REGEX} -ignore-filename-regex='${EXCLUDE_ITEM}') endforeach() endif() # Print out details of the coverage information to the command line add_custom_target( ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${LLVM_COV_PATH} show $ ${SO_OBJECTS} -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata -show-line-counts-or-regions ${EXCLUDE_REGEX} DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) # Print out a summary of the coverage information to the command line add_custom_target( ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${LLVM_COV_PATH} report $ ${SO_OBJECTS} -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata ${EXCLUDE_REGEX} DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) # Export coverage information so continuous integration tools (e.g. # Jenkins) can consume it add_custom_target( ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${LLVM_COV_PATH} export $ ${SO_OBJECTS} -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata -format="text" ${EXCLUDE_REGEX} > ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) # Generates HTML output of the coverage information for perusal add_custom_target( ccov-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${LLVM_COV_PATH} show $ ${SO_OBJECTS} -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata -show-line-counts-or-regions -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} -format="html" ${EXCLUDE_REGEX} DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info" ) # Run the executable, generating coverage information add_custom_target( ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND $ ${target_code_coverage_ARGS} DEPENDS ${TARGET_NAME}) # Generate exclusion string for use foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} '${EXCLUDE_ITEM}') endforeach() if(EXCLUDE_REGEX) set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file ${COVERAGE_INFO}) else() set(EXCLUDE_COMMAND ;) endif() if(NOT ${target_code_coverage_EXTERNAL}) set(EXTERNAL_OPTION --no-external) endif() # Capture coverage data if(${CMAKE_VERSION} VERSION_LESS "3.17.0") add_custom_target( ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters COMMAND $ ${target_code_coverage_ARGS} COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file ${COVERAGE_INFO} COMMAND ${EXCLUDE_COMMAND} DEPENDS ${TARGET_NAME}) else() add_custom_target( ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters COMMAND $ ${target_code_coverage_ARGS} COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file ${COVERAGE_INFO} COMMAND ${EXCLUDE_COMMAND} DEPENDS ${TARGET_NAME}) endif() # Generates HTML output of the coverage information for perusal add_custom_target( ccov-${target_code_coverage_COVERAGE_TARGET_NAME} COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} ${COVERAGE_INFO} DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}) endif() add_custom_command( TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME} POST_BUILD COMMAND ; COMMENT "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report." ) # AUTO if(target_code_coverage_AUTO) if(NOT TARGET ccov) add_custom_target(ccov) endif() add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME}) if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU") if(NOT TARGET ccov-report) add_custom_target(ccov-report) endif() add_dependencies( ccov-report ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}) endif() endif() # ALL if(target_code_coverage_ALL) if(NOT TARGET ccov-all-processing) message( FATAL_ERROR "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." ) endif() add_dependencies(ccov-all-processing ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) endif() endif() endif() endfunction() # Adds code coverage instrumentation to all targets in the current directory and # any subdirectories. To add coverage instrumentation to only specific targets, # use `target_code_coverage`. function(add_code_coverage) if(CODE_COVERAGE) if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") add_compile_options(-fprofile-instr-generate -fcoverage-mapping) add_link_options(-fprofile-instr-generate -fcoverage-mapping) elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") add_compile_options( -fprofile-arcs -ftest-coverage $<$:-fno-elide-constructors> -fno-default-inline) link_libraries(gcov) endif() endif() endfunction() # Adds the 'ccov-all' type targets that calls all targets added via # `target_code_coverage` with the `ALL` parameter, but merges all the coverage # data from them into a single large report instead of the numerous smaller # reports. Also adds the ccov-all-capture Generates an all-merged.info file, for # use with coverage dashboards (e.g. codecov.io, coveralls). # ~~~ # Optional: # EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! # ~~~ function(add_code_coverage_all_targets) # Argument parsing set(multi_value_keywords EXCLUDE) cmake_parse_arguments(add_code_coverage_all_targets "" "" "${multi_value_keywords}" ${ARGN}) if(CODE_COVERAGE) if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") # Merge the profile data for all of the run executables if(WIN32) add_custom_target( ccov-all-processing COMMAND powershell -Command $$FILELIST = Get-Content ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse $$FILELIST) else() add_custom_target( ccov-all-processing COMMAND ${LLVM_PROFDATA_PATH} merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`) endif() # Regex exclude only available for LLVM >= 7 if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) set(EXCLUDE_REGEX ${EXCLUDE_REGEX} -ignore-filename-regex='${EXCLUDE_ITEM}') endforeach() endif() # Print summary of the code coverage information to the command line if(WIN32) add_custom_target( ccov-all-report COMMAND powershell -Command $$FILELIST = Get-Content ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe report $$FILELIST -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata ${EXCLUDE_REGEX} DEPENDS ccov-all-processing) else() add_custom_target( ccov-all-report COMMAND ${LLVM_COV_PATH} report `cat ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata ${EXCLUDE_REGEX} DEPENDS ccov-all-processing) endif() # Export coverage information so continuous integration tools (e.g. # Jenkins) can consume it add_custom_target( ccov-all-export COMMAND ${LLVM_COV_PATH} export `cat ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -format="text" ${EXCLUDE_REGEX} > ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json DEPENDS ccov-all-processing) # Generate HTML output of all added targets for perusal if(WIN32) add_custom_target( ccov-all COMMAND powershell -Command $$FILELIST = Get-Content ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show $$FILELIST -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -show-line-counts-or-regions -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged -format="html" ${EXCLUDE_REGEX} DEPENDS ccov-all-processing) else() add_custom_target( ccov-all COMMAND ${LLVM_COV_PATH} show `cat ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -show-line-counts-or-regions -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged -format="html" ${EXCLUDE_REGEX} DEPENDS ccov-all-processing) endif() elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info") # Nothing required for gcov add_custom_target(ccov-all-processing COMMAND ;) # Exclusion regex string creation set(EXCLUDE_REGEX) foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} '${EXCLUDE_ITEM}') endforeach() if(EXCLUDE_REGEX) set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file ${COVERAGE_INFO}) else() set(EXCLUDE_COMMAND ;) endif() # Capture coverage data if(${CMAKE_VERSION} VERSION_LESS "3.17.0") add_custom_target( ccov-all-capture COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture --output-file ${COVERAGE_INFO} COMMAND ${EXCLUDE_COMMAND} DEPENDS ccov-all-processing) else() add_custom_target( ccov-all-capture COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture --output-file ${COVERAGE_INFO} COMMAND ${EXCLUDE_COMMAND} DEPENDS ccov-all-processing) endif() # Generates HTML output of all targets for perusal add_custom_target( ccov-all COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged ${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR} DEPENDS ccov-all-capture) endif() add_custom_command( TARGET ccov-all POST_BUILD COMMAND ; COMMENT "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report." ) endif() endfunction() glaze-4.4.3/cmake/dev-mode.cmake000066400000000000000000000016521475520231000164110ustar00rootroot00000000000000enable_language(CXX) # Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24: if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") cmake_policy(SET CMP0135 NEW) endif() set_property(GLOBAL PROPERTY USE_FOLDERS YES) include(CTest) if(BUILD_TESTING) add_subdirectory(tests) endif() if (glaze_ENABLE_FUZZING) add_subdirectory(fuzzing) endif() # Done in developer mode only, so users won't be bothered by this :) file(GLOB_RECURSE headers CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/include/glaze/*.hpp") source_group(TREE "${PROJECT_SOURCE_DIR}/include" PREFIX headers FILES ${headers}) file(GLOB_RECURSE sources CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/*.cpp") source_group(TREE "${PROJECT_SOURCE_DIR}/src" PREFIX sources FILES ${sources}) add_executable(glaze_ide ${sources} ${headers}) target_link_libraries(glaze_ide PRIVATE glaze::glaze) set_target_properties(glaze_glaze glaze_ide PROPERTIES FOLDER ProjectTargets) glaze-4.4.3/cmake/install-config.cmake000066400000000000000000000000671475520231000176210ustar00rootroot00000000000000include("${CMAKE_CURRENT_LIST_DIR}/glazeTargets.cmake")glaze-4.4.3/cmake/install-rules.cmake000066400000000000000000000025531475520231000175100ustar00rootroot00000000000000# 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() call for consumers to find this project set(package glaze) install( DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" COMPONENT glaze_Development ) install( TARGETS glaze_glaze EXPORT glazeTargets 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( glaze_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/${package}" CACHE PATH "CMake package config location relative to the install prefix" ) mark_as_advanced(glaze_INSTALL_CMAKEDIR) install( FILES cmake/install-config.cmake DESTINATION "${glaze_INSTALL_CMAKEDIR}" RENAME "${package}Config.cmake" COMPONENT glaze_Development ) install( FILES "${PROJECT_BINARY_DIR}/${package}ConfigVersion.cmake" DESTINATION "${glaze_INSTALL_CMAKEDIR}" COMPONENT glaze_Development ) install( EXPORT glazeTargets NAMESPACE glaze:: DESTINATION "${glaze_INSTALL_CMAKEDIR}" COMPONENT glaze_Development ) if(PROJECT_IS_TOP_LEVEL) include(CPack) endif() glaze-4.4.3/cmake/prelude.cmake000066400000000000000000000004731475520231000163510ustar00rootroot00000000000000# ---- 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() glaze-4.4.3/cmake/project-is-top-level.cmake000066400000000000000000000002321475520231000206660ustar00rootroot00000000000000# This variable is set by project() in CMake 3.21+ string( COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}" PROJECT_IS_TOP_LEVEL ) glaze-4.4.3/cmake/variables.cmake000066400000000000000000000022571475520231000166630ustar00rootroot00000000000000include(CMakeDependentOption) # ---- Developer mode ---- # Developer mode enables targets and code paths in the CMake scripts that are # only relevant for the developer(s) of glaze # Targets necessary to build the project must be provided unconditionally, so # consumers can trivially build and package the project if(PROJECT_IS_TOP_LEVEL) cmake_dependent_option( glaze_DEVELOPER_MODE "Enable developer mode" ON "PROJECT_IS_TOP_LEVEL" OFF ) cmake_dependent_option( glaze_ENABLE_FUZZING "Enable building fuzzers" ON "PROJECT_IS_TOP_LEVEL" 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( glaze_INCLUDES_WITH_SYSTEM "Use SYSTEM modifier for glaze's includes, disabling warnings" ON ) mark_as_advanced(glaze_INCLUDES_WITH_SYSTEM) if(glaze_INCLUDES_WITH_SYSTEM) set(warning_guard SYSTEM) endif() endif() glaze-4.4.3/docs/000077500000000000000000000000001475520231000135535ustar00rootroot00000000000000glaze-4.4.3/docs/FAQ.md000066400000000000000000000030611475520231000145040ustar00rootroot00000000000000# Frequently Asked Questions (FAQ) ### What happens when the JSON input is missing objects? The input JSON can be partial. It could include a single value. Only what is included in the JSON will be changed. JSON pointer syntax is also supported, which can be used to change a single element in an array (not possible with normal JSON). ### How are unknown keys handled? By default unknown keys are considered errors. A compile time switch will turn this off and just skip unknown keys: `glz::read(...)` ### Can I specify default values? Yes, whatever defaults are on your C++ structures will be the defaults of the JSON output. ```c++ struct my_struct { std::string my_string = "my default value"; }; ``` ### Do I need to specify the complete structure of the parsed JSON or just the parts I‘m interested in? You only need to specify the portion that you want to be serialized. You can have whatever else in your struct and choose to not expose it (using `glz::meta`). ### Is the write/read API deterministic? *If I parse and serialize the same JSON string multiple times, is the output guaranteed to be the same every time?* Yes, Glaze is deterministic and can be round tripped, but in some cases C++ containers do not guarantee identical roundtrip behavior. Structs are compile time known, so they're deterministic. You can use std::map and std::unordered_map containers with the library. If you choose the former the sequence is deterministic, but not the latter. Floating point numbers use round-trippable algorithms. glaze-4.4.3/docs/JMESPath.md000066400000000000000000000104131475520231000154470ustar00rootroot00000000000000# JMESPath Support [JMESPath](https://jmespath.org/tutorial.html) is a query language for JSON. Glaze currently has limited support for partial reading with JMESPath. Broader support will be added in the future. - Compile time evaluated JMESPath expressions offer excellent performance. Example: ```c++ struct Person { std::string first_name{}; std::string last_name{}; uint16_t age{}; }; struct Family { Person father{}; Person mother{}; std::vector children{}; }; struct Home { Family family{}; std::string address{}; }; suite jmespath_read_at_tests = [] { "compile-time read_jmespath"_test = [] { Home home{.family = {.father = {"Gilbert", "Fox", 28}, .mother = {"Anne", "Fox", 30}, .children = {{"Lilly"}, {"Vincent"}}}}; std::string buffer{}; expect(not glz::write_json(home, buffer)); std::string first_name{}; auto ec = glz::read_jmespath<"family.father.first_name">(first_name, buffer); expect(not ec) << glz::format_error(ec, buffer); expect(first_name == "Gilbert"); Person child{}; expect(not glz::read_jmespath<"family.children[0]">(child, buffer)); expect(child.first_name == "Lilly"); expect(not glz::read_jmespath<"family.children[1]">(child, buffer)); expect(child.first_name == "Vincent"); }; "run-time read_jmespath"_test = [] { Home home{.family = {.father = {"Gilbert", "Fox", 28}, .mother = {"Anne", "Fox", 30}, .children = {{"Lilly"}, {"Vincent"}}}}; std::string buffer{}; expect(not glz::write_json(home, buffer)); std::string first_name{}; auto ec = glz::read_jmespath("family.father.first_name", first_name, buffer); expect(not ec) << glz::format_error(ec, buffer); expect(first_name == "Gilbert"); Person child{}; expect(not glz::read_jmespath("family.children[0]", child, buffer)); expect(child.first_name == "Lilly"); expect(not glz::read_jmespath("family.children[1]", child, buffer)); expect(child.first_name == "Vincent"); }; }; ``` ## Run-time Expressions It can be expensive to tokenize at runtime. The runtime version of `glz::read_jmespath` takes in a `const glz::jmespath_expression&`. This allows expressions to be pre-computed, reused, and cached for better runtime performance. ```c++ Person child{}; // A runtime expression can be pre-computed and saved for more efficient lookups glz::jmespath_expression expression{"family.children[0]"}; expect(not glz::read_jmespath(expression, child, buffer)); expect(child.first_name == "Lilly"); ``` Note that this still works: ```c++ expect(not glz::read_jmespath("family.children[0]", child, buffer)); ``` ## Slices ```c++ suite jmespath_slice_tests = [] { "slice compile-time"_test = [] { std::vector data{0,1,2,3,4,5,6,7,8,9}; std::string buffer{}; expect(not glz::write_json(data, buffer)); std::vector slice{}; expect(not glz::read_jmespath<"[0:5]">(slice, buffer)); expect(slice.size() == 5); expect(slice[0] == 0); expect(slice[1] == 1); expect(slice[2] == 2); expect(slice[3] == 3); expect(slice[4] == 4); }; "slice run-time"_test = [] { std::vector data{0,1,2,3,4,5,6,7,8,9}; std::string buffer{}; expect(not glz::write_json(data, buffer)); std::vector slice{}; expect(not glz::read_jmespath("[0:5]", slice, buffer)); expect(slice.size() == 5); expect(slice[0] == 0); expect(slice[1] == 1); expect(slice[2] == 2); expect(slice[3] == 3); expect(slice[4] == 4); }; "slice compile-time multi-bracket"_test = [] { std::vector> data{{1,2},{3,4,5},{6,7}}; std::string buffer{}; expect(not glz::write_json(data, buffer)); int v{}; expect(not glz::read_jmespath<"[1][2]">(v, buffer)); expect(v == 5); }; "slice run-time multi-bracket"_test = [] { std::vector> data{{1,2},{3,4,5},{6,7}}; std::string buffer{}; expect(not glz::write_json(data, buffer)); int v{}; expect(not glz::read_jmespath("[1][2]", v, buffer)); expect(v == 5); }; }; ``` glaze-4.4.3/docs/JSON/000077500000000000000000000000001475520231000143245ustar00rootroot00000000000000glaze-4.4.3/docs/JSON/json-integer-parsing.md000066400000000000000000000017321475520231000207160ustar00rootroot00000000000000# JSON Integer Parsing JSON integer parsing in Glaze rejects negative exponents and decimal values. Valid examples for a `uint8_t`: ``` 0 123 255 1e1 1e+2 ``` Invalid examples for a `uint8_t`: ``` 0.0 ** decimal 256 ** out of range 1.0 ** decimal 1e-2 ** negative exponent ``` This applies for larger integer types as well, such as `int64_t`. Glaze rejects negative exponents and decimal values for the following reasons: - Fractional values cannot be stored exactly in integers, so data loss can be unknown to the developer. - Fractional values should be rounded in different ways or truncated when converted to integers based on the application. It is better to parse these values as floating point values and then apply specific conversions. - The parser runs faster - We are able to disambiguate between values that can be stored in integer types and floating point types, which allows auto variant deduction for variants containing both integers and floating point values. glaze-4.4.3/docs/binary.md000066400000000000000000000027021475520231000153620ustar00rootroot00000000000000# Binary Format (BEVE) Glaze provides a binary format to send and receive messages like JSON, but with significantly improved performance and message size savings. The binary specification is known as [BEVE](https://github.com/beve-org/beve). **Write BEVE** ```c++ my_struct s{}; std::vector buffer{}; glz::write_beve(s, buffer); ``` **Read BEVE** ```c++ my_struct s{}; glz::read_beve(s, buffer); ``` > [!NOTE] > > As of v3.3.0 reading binary is safe for invalid input and doesn't require null terminated buffers. ## Untagged Binary By default Glaze will handle structs as tagged objects, meaning that keys will be written/read. However, structs can be written/read without tags by using the option `structs_as_arrays` or the functions `glz::write_beve_untagged` and `glz::read_beve_untagged`. ## BEVE to JSON Conversion `glaze/binary/beve_to_json.hpp` provides `glz::beve_to_json`, which directly converts a buffer of BEVE data to a buffer of JSON data. ## Partial Objects It is sometimes desirable to write out only a portion of an object. This is permitted via an array of JSON pointers, which indicate which parts of the object should be written out. ```c++ static constexpr auto partial = glz::json_ptrs("/i", "/d", "/sub/x", "/sub/y"); std::vector out; glz::write_beve(s, out); ``` glaze-4.4.3/docs/cli-menu.md000066400000000000000000000040661475520231000156140ustar00rootroot00000000000000# Command Line Interface (CLI) Menu Glaze makes it easy to generate command line interface menus from nested structs. Make structs of callable and combine them to build your menu hierarchy. The command names will be reflected from the function names or use a `glz::meta` that you provide. ## Clearing The Screen To bring the menu to the forefront, simply type `cls` or `clear` and press `ENTER`. ## Example ```c++ struct my_functions { std::function hello = [] { std::printf("Hello\n"); }; std::function world = [] { std::printf("World\n"); }; }; // This glz::meta is optional unless you need to change the function name in the menu template <> struct glz::meta { using T = my_functions; static constexpr auto value = object("hi", &T::hello, &T::world); }; struct four_t { four_t(glz::make_reflectable) {} // required for reflection since this struct has no members std::pair operator()() { return {"four", 4}; } }; struct more_functions { std::function one = [] { return "one"; }; std::function two = [] { return 2; }; std::function three = [] { return "three"; }; four_t four{}; }; struct a_special_function { a_special_function(glz::make_reflectable) {} glz::raw_json operator()(const std::tuple& in) { return std::to_string(std::get<0>(in)) + " | " + std::to_string(std::get<1>(in)); } }; struct my_nested_menu { my_functions first_menu{}; more_functions second_menu{}; std::function user_number = [](int x) { return x; }; std::function user_string = [](const auto& str) { return str; }; a_special_function special{}; }; void nested_menu() { my_nested_menu menu{}; glz::run_cli_menu(menu); // show the command line interface menu } ``` The top menu when printed to console will look like: ``` ================================ 1 first_menu 2 second_menu 3 user_number 4 user_string 5 special 6 Exit Menu -------------------------------- cmd> ``` glaze-4.4.3/docs/csv.md000066400000000000000000000032071475520231000146720ustar00rootroot00000000000000# CSV (Comma Separated Values) Glaze supports [CSV](https://en.wikipedia.org/wiki/Comma-separated_values) reading and writing for structs and standard map types. By default Glaze writes out structures row-wise, which is more efficient if all the data is available at once. Column-wise CSV format is also supported. > Note that if you want containers to have their CSV data aligned, they need to be the same length. ## API ```c++ glz::read_csv(obj, buffer); // row-wise glz::read_csv(obj, buffer); // column-wise glz::write_csv(obj, buffer); // row-wise glz::write_csv(obj, buffer); // column-wise ``` ## Example ```c++ struct my_struct { std::vector num1{}; std::deque num2{}; std::vector maybe{}; std::vector> v3s{}; }; ``` Reading/Writing column-wise: ```c++ std::string input_col = R"(num1,num2,maybe,v3s[0],v3s[1],v3s[2] 11,22,1,1,1,1 33,44,1,2,2,2 55,66,0,3,3,3 77,88,0,4,4,4)"; my_struct obj{}; glz::read_csv(obj, input_col); expect(obj.num1[0] == 11); expect(obj.num2[2] == 66); expect(obj.maybe[3] == false); expect(obj.v3s[0] == std::array{1, 1, 1}); expect(obj.v3s[1] == std::array{2, 2, 2}); expect(obj.v3s[2] == std::array{3, 3, 3}); expect(obj.v3s[3] == std::array{4, 4, 4}); std::string out{}; glz::write(obj, out); expect(out == R"(num1,num2,maybe,v3s[0],v3s[1],v3s[2] 11,22,1,1,1,1 33,44,1,2,2,2 55,66,0,3,3,3 77,88,0,4,4,4 )"); expect(!glz::write_file_csv(obj, "csv_test_colwise.csv", std::string{})); ``` glaze-4.4.3/docs/custom-serialization.md000066400000000000000000000062501475520231000202650ustar00rootroot00000000000000# Custom Serialization/Deserialization See [Custom Read/Write](https://github.com/stephenberry/glaze/tree/main#custom-readwrite) in the main README.md for using `glz::custom`. Advanced custom serialization/deserialization is achievable by implementing your own `from` and `to` specializations within the glz::detail namespace. > Note that the API within the `detail` namespace is subject to change and is therefore not expected to be as stable. Example: ```c++ struct date { uint64_t data; std::string human_readable; }; template <> struct glz::meta { using T = date; static constexpr auto value = object("date", &T::human_readable); }; namespace glz::detail { template <> struct from { template static void op(date& value, auto&&... args) { read::op(value.human_readable, args...); value.data = std::stoi(value.human_readable); } }; template <> struct to { template static void op(date& value, auto&&... args) noexcept { value.human_readable = std::to_string(value.data); write::op(value.human_readable, args...); } }; } void example() { date d{}; d.data = 55; std::string s{}; glz::write_json(d, s); expect(s == R"("55")"); d.data = 0; glz::read_json(d, s); expect(d.data == 55); } ``` Notes: The templated `Opts` parameter contains the compile time options. The `args...` can be broken out into the runtime context (runtime options), iterators, and the buffer index when reading. ## UUID Example Say we have a UUID library for converting a `uuid_t` from a `std::string_view` and to a `std::string`. ```c++ namespace glz::detail { template <> struct from { template static void op(uuid_t& uuid, auto&&... args) { // Initialize a string_view with the appropriately lengthed buffer // Alternatively, use a std::string for any size (but this will allocate) std::string_view str = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; read::op(str, args...); uuid = uuid_lib::uuid_from_string_view(str); } }; template <> struct to { template static void op(const uuid_t& uuid, auto&&... args) noexcept { std::string str = uuid_lib::uuid_to_string(uuid); write::op(str, args...); } }; } ``` # Handling Ambiguous Partial Specialization You may want to custom parse a class that matches an underlying glaze partial specialization for a template like below: ```c++ template requires readable_array_t struct from_json ``` If your own parsing function desires partial template specialization, then ambiguity may occur: ```c++ template requires std::derived_from struct from_json ``` To solve this problem, glaze will check for `custom_read` or `custom_write` values within `glz::meta` and remove the ambiguity and use the custom parser. ```c++ template requires std::derived_from struct glz::meta { static constexpr auto custom_read = true; }; ``` glaze-4.4.3/docs/custom-wrappers.md000066400000000000000000000033011475520231000172450ustar00rootroot00000000000000# Custom Wrappers Custom wrappers are used to change the serialization/deserialization on basic types (e.g. int, double, std::string). > Note that the API within the `detail` namespace is subject to change and is therefore not expected to be as stable. The example below shows how numbers can be read in via strings by providing a wrapper to indicate the proper serialization/deserialization. ```c++ template struct quoted_t { T& val; }; namespace glz::detail { template struct from_json> { template static void op(auto&& value, auto&&... args) { skip_ws(args...); match<'"'>(args...); read::op(value.val, args...); match<'"'>(args...); } }; template struct to_json> { template static void op(auto&& value, is_context auto&& ctx, auto&&... args) noexcept { dump<'"'>(args...); write::op(value.val, ctx, args...); dump<'"'>(args...); } }; } template constexpr decltype(auto) qouted() { return [](auto&& val) { return quoted_t>{val.*MemPtr}; }; } struct A { double x; }; template <> struct glz::meta { static constexpr auto value = object("x", qouted<&A::x>()); // or... //static constexpr auto value = object("x", [](auto&& val) { return qouted_t(val.x); }); }; void example() { A a{3.14}; std::string buffer{}; glz::write_json(a, buffer); expect(buffer == R"({"x":"3.14"})"); buffer = R"({"x":"999.2"})"; expect(glz::read_json(a, buffer) == glz::error_code::none); expect(a.x == 999.2); } ``` glaze-4.4.3/docs/directory-io.md000066400000000000000000000020001475520231000164760ustar00rootroot00000000000000# Directory Serialization and Deserialization Glaze supports reading and writing from an entire directory of files into a map like object. `#include "glaze/glaze.hpp"` or specifically include: - `#include "glaze/file/read_directory.hpp"` for read support - `#include "glaze/file/write_directory.hpp"` for write support The key to the map is the path to the individual file and the value is any C++ type. The code below demonstrates writing out a map of two file paths and associated structs to two separate files. We then read the created directory into a default structure of the same form. ```c++ std::map files{{"./dir/alpha.json", {}}, {"./dir/beta.json", {.i = 0}}}; expect(not glz::write_directory(files, "./dir")); std::map input{}; expect(not glz::read_directory(input, "./dir")); expect(input.size() == 2); expect(input.contains("./dir/alpha.json")); expect(input.contains("./dir/beta.json")); expect(input["./dir/beta.json"].i == 0); ``` glaze-4.4.3/docs/exceptions.md000066400000000000000000000007031475520231000162560ustar00rootroot00000000000000# Glaze Exceptions If C++ exceptions are being used in your codebase, it can be cleaner to call functions that throw. Glaze provides a header `glaze_exceptions.hpp`, which provides an API to use Glaze with exceptions. This header will be disabled if `-fno-exceptions` is specified. > Glaze functions that throw exceptions use the namespace `glz::ex` ```c++ std::string buffer{}; glz::ex::read_json(obj, buffer); // will throw on a read error ``` glaze-4.4.3/docs/generic-json.md000066400000000000000000000062111475520231000164600ustar00rootroot00000000000000# Generic JSON While Glaze is focused on strongly typed data, there is basic support for completely generic JSON. If absolutely nothing is known about the JSON structure, then [glz::json_t](https://github.com/stephenberry/glaze/blob/main/include/glaze/json/json_t.hpp) may be helpful, but it comes at a performance cost due to the use of dynamic memory allocations. ```c++ glz::json_t json{}; std::string buffer = R"([5,"Hello World",{"pi":3.14}])"; glz::read_json(json, buffer); assert(json[0].get() == 5.0); assert(json[1].get() == "Hello World"); assert(json[2]["pi"].get() == 3.14); assert(json[2]["pi"].as() == 3); ``` ```c++ glz::json_t json = { {"pi", 3.141}, {"happy", true}, {"name", "Stephen"}, {"nothing", nullptr}, {"answer", {{"everything", 42.0}}}, {"list", {1.0, 0.0, 2.0}}, {"object", { {"currency", "USD"}, {"value", 42.99} }} }; std::string buffer{}; glz::write_json(json, buffer); expect(buffer == R"({"answer":{"everything":42},"happy":true,"list":[1,0,2],"name":"Stephen","object":{"currency":"USD","value":42.99},"pi":3.141})"); ``` ## get() vs as() `glz::json_t` is a variant underneath that stores all numbers in `double`. The `get()` method mimics a `std::get` call for a variant, which rejects conversions. If you want to access a number as an `int`, then call `json.as()`, which will cast the `double` to an `int`. ## Type Checking json_t `glz::json_t` has member functions to check the JSON type: - `.is_object()` - `.is_array()` - `.is_string()` - `.is_number()` - `.is_null()` There are also free functions of these, such as `glz::is_object(...)` ## .empty() Calling `.empty()` on a `json_t` value will return true if it contains an empty object, array, or string, or a null value. Otherwise, returns false. ## .size() Calling `.size()` on a `json_t` value will return the number of items in an object or array, or the size of a string. Otherwise, returns zero. ## .dump() Calling `.dump()` on a `json_t` value is equivalent to calling `glz::write_json(value)`, which returns an `expected`. ## glz::raw_json There are times when you want to parse JSON into a C++ string, to inspect or decode at a later point. `glz::raw_json` is a simple wrapper around a `std::string` that will decode and encode JSON without needing a concrete structure. ```c++ std::vector v{"0", "1", "2"}; std::string s; glz::write_json(v, s); expect(s == R"([0,1,2])"); ``` ## Using `json_t` As The Source After parsing into a `json_t` it is sometimes desirable to parse into a concrete struct or a portion of the `json_t` into a struct. Glaze allows a `json_t` value to be used as the source where a buffer would normally be passed. ```c++ auto json = glz::read_json(R"({"foo":"bar"})"); expect(json->contains("foo")); auto obj = glz::read_json>(json.value()); // This reads the json_t into a std::map ``` Another example: ```c++ glz::json_t json{}; expect(not glz::read_json(json, R"("Beautiful beginning")")); std::string v{}; expect(not glz::read(v, json)); expect(v == "Beautiful beginning"); ``` glaze-4.4.3/docs/glaze-interfaces.md000066400000000000000000000123101475520231000173150ustar00rootroot00000000000000# Glaze Interfaces (Generic Library API) Glaze has been designed to work as a generic interface for shared libraries and more. This is achieved through JSON pointer syntax access to memory. Glaze allows a single header API (`api.hpp`) to be used for every shared library interface, greatly simplifying shared library handling. > Interfaces are simply Glaze object types. So whatever any JSON/binary interface can automatically be used as a library API. The API is shown below. It is simple, yet incredibly powerful, allowing pretty much any C++ class to be manipulated across the API via JSON or binary, or even the class itself to be passed and safely cast on the other side. ```c++ struct api { /*default constructors hidden for brevity*/ template [[nodiscard]] T* get(const sv path) noexcept; // Get a std::function from a member function across the API template [[nodiscard]] T get_fn(const sv path); template [[nodiscard]] Ret call(const sv path, Args&&... args); virtual bool read(const uint32_t /*format*/, const sv /*path*/, const sv /*data*/) noexcept = 0; virtual bool write(const uint32_t /*format*/, const sv /*path*/, std::string& /*data*/) = 0; [[nodiscard]] virtual const sv last_error() const noexcept { return error; } protected: /// unchecked void* access virtual void* get(const sv path, const sv type_hash) noexcept = 0; virtual bool caller(const sv path, const sv type_hash, void* ret, std::span args) noexcept = 0; virtual std::unique_ptr get_fn(const sv path, const sv type_hash) noexcept = 0; std::string error{}; }; ``` ## Member Functions Member functions can be registered with the metadata, which allows the function to be called across the API. ```c++ struct my_api { int func() { return 5; }; }; template <> struct glz::meta { using T = my_api; static constexpr auto value = object("func", &T::func); static constexpr std::string_view name = "my_api"; }; ``` In use: ```c++ std::shared_ptr iface{ glz_iface()() }; auto io = (*iface)["my_api"](); expect(5 == io->call("/func")); ``` `call` invokes the function across the API. Arguments are also allowed in the call function: ```c++ auto str = io->call("/concat", "Hello", "World"); ``` `get_fn` provides a means of getting a `std::function` from a member function across the API. This can be more efficient if you intend to call the same function multiple times. ```c++ auto f = io->get_fn>("/func"); ``` ## std::function Glaze allows `std::function` types to be added to objects and arrays in order to use them across Glaze APIs. ```c++ int x = 7; double y = 5.5; auto& f = io->get>("/f"); expect(f(x, y) == 38.5); ``` ## Type Safety A valid interface concern is binary compatibility between types. Glaze uses compile time hashing of types that is able to catch a wide range of changes to classes or types that would cause binary incompatibility. These compile time hashes are checked when accessing across the interface and provide a safeguard, much like a `std::any_cast`, but working across compilations.`std::any_cast` does not guarantee any safety between separately compiled code, whereas Glaze adds significant type checking across compilations and versions of compilers. ## Name By default custom type names from `glz::name_v` will be `"Unnamed"`. It is best practice to give types the same name as it has in C++, including the namespace (at least the local namespace). Concepts exist for naming `const`, pointer (`*`), and reference (`&`), versions of types as they are used. Many standard library containers are also supported. ```c++ expect(glz::name_v> == "std::vector"); ``` To add a name for your class, include it in the `glz::meta`: ```c++ template <> struct glz::meta { static constexpr std::string_view name = "my_api"; }; ``` Or, include it via local glaze meta: ```c++ struct my_api { struct glaze { static constexpr std::string_view name = "my_api"; }; }; ``` ## Version By default all types get a version of `0.0.1`. The version tag allows the user to intentionally break API compatibility for a type when making changes that would not be caught by the compile time type checking. ```c++ template <> struct glz::meta { static constexpr glz::version_t version{ 0, 0, 2 }; }; ``` Or, include it locally like `name` or `value`. ## What Is Checked? Glaze catches the following changes: - `name` in meta - `version` in meta - the `sizeof` the type - All member variables names (for object types) - The compiler (clang, gcc, msvc) - `std::is_trivial` - `std::is_standard_layout` - `std::is_default_constructible` - `std::is_trivially_default_constructible` - `std::is_nothrow_default_constructible` - `std::is_trivially_copyable` - `std::is_move_constructible` - `std::is_trivially_move_constructible` - `std::is_nothrow_move_constructible` - `std::is_destructible` - `std::is_trivially_destructible` - `std::is_nothrow_destructible` - `std::has_unique_object_representations` - `std::is_polymorphic` - `std::has_virtual_destructor` - `std::is_aggregate` glaze-4.4.3/docs/json-include.md000066400000000000000000000062661475520231000165010ustar00rootroot00000000000000# JSON Include System When using JSON for configuration files it can be helpful to move object definitions into separate files. This reduces copying and the need to change inputs across multiple files. Glaze provides a `glz::file_include` type that can be added to your struct (or glz::meta). The key may be anything, in this example we use choose `include` to mimic C++. ```c++ struct includer_struct { glz::file_include include{}; std::string str = "Hello"; int i = 55; }; ``` When this object is parsed, when the key `include` is encountered the associated file will be read into the local object. ```c++ includer_struct obj{}; std::string s = R"({"include": "./obj.json", "i": 100})"; glz::read_json(obj, s); ``` This will read the `./obj.json` file into the `obj` as it is parsed. Since glaze parses in sequence, the order in which includes are listed in the JSON file is the order in which they will be evaluated. The `file_include` will only be read into the local object, so includes can be deeply nested. > Paths are always relative to the location of the previously loaded file. For nested includes this means the user only needs to consider the relative path to the file in which the include is written. ## Hostname Include Similar to `glz::file_include`, glaze provides `glz::hostname_include`. This is used for host-specific configuration files. > You must explicitly include the header file `#include "glaze/file/hostname_include.hpp"` In use: ```c++ std::string s = R"({"hostname_include": "../{}_config.json", "i": 100})"; auto ec = glz::read_json(obj, s); ``` Note how the provided path contains `{}`, which will be replaced with the hostname when the file is read. Other than this, the `hostname_include` behaves the same as `file_include`. ## glz::raw_or_file `glz::raw_or_file` is somewhat like a `glz::file_include`, but also handles serialization. The purpose is to allow JSON files to be optionally referred to within higher level configuration files, but once loaded, the files behave as glz::raw_json, where the format does not need to be understood until deserialized. > Note that his approach only allows a single layer of includes. It does not allow infinitely deep includes like `glz::file_include` and `glz::hostname_include` support. **How glz::raw_or_file behaves:** If the input is a valid file path within a string, the entire file will be read into a std::string within the glz::raw_or_file member. The internal buffer does not escape characters, as it is handled like glz::raw_json. If the input is not a valid file path or not a string, the value is simply validated and stored like glz::raw_json When serialized, the value is dumped like glz::raw_json, which means it will look like the input file was copied and pasted into the higher level document. Benefits of this approach include: - The layout for the file loaded into glz::raw_or_file does not need to be known by the program. As long as it is valid JSON it can be passed along. - The serialized output after loading the file looks like a combined file, which is easier to read and format than an escaped string version of the file. It is also more efficient because escaping/unescaping doesn't need to be performed, only validation. glaze-4.4.3/docs/json-pointer-syntax.md000066400000000000000000000055741475520231000200630ustar00rootroot00000000000000# JSON Pointer Syntax [Link to simple JSON pointer syntax explanation](https://github.com/stephenberry/JSON-Pointer) Glaze supports JSON pointer syntax access in a C++ context. This is extremely helpful for building generic APIs, which allows values of complex objects to be accessed without needed know the encapsulating class. ```c++ my_struct s{}; auto d = glz::get(s, "/d"); // d.value() is a std::reference_wrapper to d in the structure s ``` ```c++ my_struct s{}; glz::set(s, "/d", 42.0); // d is now 42.0 ``` > JSON pointer syntax works with deeply nested objects and anything serializable. ```c++ // Tuple Example auto tuple = std::make_tuple(3, 2.7, std::string("curry")); glz::set(tuple, "/0", 5); expect(std::get<0>(tuple) == 5.0); ``` ### read_as `read_as` allows you to read into an object from a JSON pointer and an input buffer. ```c++ Thing thing{}; glz::read_as_json(thing, "/vec3", "[7.6, 1292.1, 0.333]"); expect(thing.vec3.x == 7.6 && thing.vec3.y == 1292.1 && thing.vec3.z == 0.333); glz::read_as_json(thing, "/vec3/2", "999.9"); expect(thing.vec3.z == 999.9); ``` ### get_as_json `get_as_json` allows you to get a targeted value from within an input buffer. This is especially useful if you need to change how an object is parsed based on a value within the object. ```c++ std::string s = R"({"obj":{"x":5.5}})"; auto z = glz::get_as_json(s); expect(z == 5.5); ``` > [!IMPORTANT] > > `get_as_json` does not validate JSON beyond the targeted value. This is to avoid parsing the entire document once the targeted value is reached. ### get_sv_json `get_sv_json` allows you to get a `std::string_view` to a targeted value within an input buffer. This can be more efficient to check values and handle custom parsing than constructing a new value with `get_as_json`. ```c++ std::string s = R"({"obj":{"x":5.5}})"; auto view = glz::get_sv_json<"/obj/x">(s); expect(view == "5.5"); ``` > [!IMPORTANT] > > `get_sv_json` does not validate JSON beyond the targeted value. This is to avoid parsing the entire document once the targeted value is reached. ## write_at `write_at` allows raw text to be written to a specific JSON value pointed at via JSON Pointer syntax. ```c++ std::string buffer = R"( { "action": "DELETE", "data": { "x": 10, "y": 200 }})"; auto ec = glz::write_at<"/action">(R"("GO!")", buffer); expect(buffer == R"( { "action": "GO!", "data": { "x": 10, "y": 200 }})"); ``` Another example: ```c++ std::string buffer = R"({"str":"hello","number":3.14,"sub":{"target":"X"}})"; auto ec = glz::write_at<"/sub/target">("42", buffer); expect(not ec); expect(buffer == R"({"str":"hello","number":3.14,"sub":{"target":42}})"); ``` ## Seek `glz::seek` allows you to call a lambda on a nested value. ```c++ my_struct s{}; std::any a{}; glz::seek([&](auto& value) { a = value; }, s, "/hello"); expect(a.has_value() && std::any_cast(a) == "Hello World"); ``` glaze-4.4.3/docs/json-schema.md000066400000000000000000000032571475520231000163130ustar00rootroot00000000000000# JSON Schema JSON Schema can automaticly be generated for serializable named types exposed via the meta system. ```c++ std::string schema = glz::write_json_schema(); ``` This can be used for autocomplete, linting, and validation of user input/config files in editors like VS Code that support JSON Schema. ![autocomplete example](https://user-images.githubusercontent.com/9817348/199346159-8b127c7b-a9ac-49fe-b86d-71350f0e1b10.png) ![linting example](https://user-images.githubusercontent.com/9817348/199347118-ef7e9f74-ed20-4ff5-892a-f70ff1df23b5.png) ## Registering JSON Schema Metadata By default `glz::write_json_schema` will write out your fields and types, but additional field may also be registered by specializing `glz::json_schema` or adding a `glaze_json_schema` to your struct. Example: ```c++ struct meta_schema_t { int x{}; std::string file_name{}; bool is_valid{}; }; template <> struct glz::json_schema { schema x{.description = "x is a special integer", .minimum = 1}; schema file_name{.description = "provide a file name to load"}; schema is_valid{.description = "for validation"}; }; ``` The `glz::json_schema` struct allows additional metadata like `minimum`, `maximum`, etc. to be specified for your fields. Or you can define `glaze_json_schema` in your struct in the same manner: ```c++ struct local_schema_t { int x{}; std::string file_name{}; bool is_valid{}; struct glaze_json_schema { glz::schema x{.description = "x is a special integer", .minimum = 1}; glz::schema file_name{.description = "provide a file name to load"}; glz::schema is_valid{.description = "for validation"}; }; }; ``` glaze-4.4.3/docs/max-float-precision.md000066400000000000000000000034151475520231000177610ustar00rootroot00000000000000# Maximum Floating Point Precision (Writing) Glaze provides a compile time option (`float_max_write_precision`) to limit the precision when writing numbers to JSON. This improves performance when high precision is not needed in the resulting JSON. The wrappers `glz::write_float32` and `glz::write_float64` set this compile time option on individual numbers, arrays, or objects. The wrapper `glz::write_float_full` turns off higher level float precision wrapper options. Example unit tests: ```c++ struct write_precision_t { double pi = std::numbers::pi_v; struct glaze { using T = write_precision_t; static constexpr auto value = glz::object("pi", glz::write_float32<&T::pi>); }; }; suite max_write_precision_tests = [] { "max_write_precision"_test = [] { double pi = std::numbers::pi_v; std::string json_double = glz::write_json(pi); constexpr glz::opts options{.float_max_write_precision = glz::float_precision::float32}; std::string json_float = glz::write(pi); expect(json_double != json_float); expect(json_float == glz::write_json(std::numbers::pi_v)); expect(!glz::read_json(pi, json_float)); std::vector double_array{ pi, 2 * pi }; json_double = glz::write_json(double_array); json_float = glz::write(double_array); expect(json_double != json_float); expect(json_float == glz::write_json(std::array{std::numbers::pi_v, 2 * std::numbers::pi_v})); expect(!glz::read_json(double_array, json_float)); }; "write_precision_t"_test = [] { write_precision_t obj{}; std::string json_float = glz::write_json(obj); expect(json_float == R"({"pi":3.1415927})") << json_float; }; }; ``` glaze-4.4.3/docs/nullable-types.md000066400000000000000000000017531475520231000170430ustar00rootroot00000000000000# Nullable Types Glaze provides concepts to support custom nullable types. In order to serialize and parse your custom nullable type it should conform to the `nullable_t` concept: ```c++ template concept nullable_t = !meta_value_t && !str_t && requires(T t) { bool(t); { *t }; }; ``` > [!NOTE] > > If you want to read into a nullable type then you must have a `.reset()` method or your type must be assignable from empty braces: `value = {}`. This allows Glaze to reset the value if `"null"` is parsed. ## Nullable Value Types In some cases a type may be like a `std::optional`, but also require an `operator bool()` that converts to the internal boolean. In these cases you can instead conform to the `nullable_value_t` concept: ```c++ // For optional like types that cannot overload `operator bool()` template concept nullable_value_t = !meta_value_t && requires(T t) { t.value(); { t.has_value() } -> std::convertible_to; }; ``` glaze-4.4.3/docs/partial-read.md000066400000000000000000000150151475520231000164440ustar00rootroot00000000000000# Partial Read At times it is not necessary to read the entire JSON document, but rather just a header or some of the initial fields. This document describes a limited approach using the `partial_read` option, to quickly exit after parsing fields of interest. For more advanced (and still performant) partial reading, Glaze provides compile-time and run-time [JMESPath support](./JMESPath.md). # Partial reading with glz::opts `partial_read` is a compile time flag in `glz::opts` that indicates only existing array and object elements should be read into, and once the memory has been read, parsing returns without iterating through the rest of the document. > [!NOTE] > > Once any sub-object is read, the parsing will finish. This ensures high performance with short circuiting. > A [wrapper](./wrappers.md) by the same name also exists. Example: read only the first two elements into a `std::tuple` ```c++ std::string s = R"(["hello",88,"a string we don't care about"])"; std::tuple obj{}; expect(!glz::read(obj, s)); expect(std::get<0>(obj) == "hello"); expect(std::get<1>(obj) == 88); ``` Example: read only the first two elements into a `std::vector` ```c++ std::string s = R"([1,2,3,4,5])"; std::vector v(2); expect(!glz::read(v, s)); expect(v.size() == 2); expect(v[0] = 1); expect(v[1] = 2); ``` Example: read only the allocated element in a `std::map` ```c++ std::string s = R"({"1":1,"2":2,"3":3})"; std::map obj{{"2", 0}}; expect(!glz::read(obj, s)); expect(obj.size() == 1); expect(obj.at("2") = 2); ``` Example: read only the fields present in a struct and then short circuit the parse ```c++ struct partial_struct { std::string string{}; int32_t integer{}; }; ``` ```c++ std::string s = R"({"integer":400,"string":"ha!",ignore})"; partial_struct obj{}; expect(!glz::read(obj, s)); expect(obj.string == "ha!"); expect(obj.integer == 400); ``` ## Unit Test Examples ```c++ struct Header { std::string id{}; std::string type{}; }; struct HeaderFlipped { std::string type{}; std::string id{}; }; struct NestedPartialRead { std::string method{}; Header header{}; int number{}; }; suite partial_read_tests = [] { using namespace ut; static constexpr glz::opts partial_read{.partial_read = true}; "partial read"_test = [] { Header h{}; std::string buf = R"({"id":"51e2affb","type":"message_type","unknown key":"value"})"; expect(!glz::read(h, buf)); expect(h.id == "51e2affb"); expect(h.type == "message_type"); }; "partial read 2"_test = [] { Header h{}; // closing curly bracket is missing std::string buf = R"({"id":"51e2affb","type":"message_type","unknown key":"value")"; expect(!glz::read(h, buf)); expect(h.id == "51e2affb"); expect(h.type == "message_type"); }; "partial read unknown key"_test = [] { Header h{}; std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type"})"; expect(glz::read(h, buf) == glz::error_code::unknown_key); expect(h.id == "51e2affb"); expect(h.type.empty()); }; "partial read unknown key 2"_test = [] { Header h{}; std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type"})"; expect(!glz::read(h, buf)); expect(h.id == "51e2affb"); expect(h.type == "message_type"); }; "partial read don't read garbage"_test = [] { Header h{}; std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type"garbage})"; expect(!glz::read(h, buf)); expect(h.id == "51e2affb"); expect(h.type == "message_type"); }; "partial read missing key"_test = [] { Header h{}; std::string buf = R"({"id":"51e2affb","unknown key":"value"})"; expect(glz::read(h, buf) != glz::error_code::missing_key); expect(h.id == "51e2affb"); expect(h.type.empty()); }; "partial read missing key 2"_test = [] { Header h{}; std::string buf = R"({"id":"51e2affb","unknown key":"value"})"; expect(!glz::read(h, buf)); expect(h.id == "51e2affb"); expect(h.type.empty()); }; "partial read HeaderFlipped"_test = [] { HeaderFlipped h{}; std::string buf = R"({"id":"51e2affb","type":"message_type","unknown key":"value"})"; expect(not glz::read(h, buf)); expect(h.id == "51e2affb"); expect(h.type == "message_type"); }; "partial read HeaderFlipped unknown key"_test = [] { HeaderFlipped h{}; std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type"})"; expect(glz::read(h, buf) == glz::error_code::unknown_key); expect(h.id == "51e2affb"); expect(h.type.empty()); }; "partial read unknown key 2 HeaderFlipped"_test = [] { HeaderFlipped h{}; std::string buf = R"({"id":"51e2affb","unknown key":"value","type":"message_type","another_field":409845})"; expect(glz::read(h, buf) == glz::error_code::none); expect(h.id == "51e2affb"); expect(h.type == "message_type"); }; }; suite nested_partial_read_tests = [] { using namespace ut; static constexpr glz::opts partial_read{.partial_read = true}; "nested object partial read"_test = [] { NestedPartialRead n{}; std::string buf = R"({"method":"m1","header":{"id":"51e2affb","type":"message_type","unknown key":"value"},"number":51})"; expect(not glz::read(n, buf)); expect(n.method == "m1"); expect(n.header.id == "51e2affb"); expect(n.header.type == "message_type"); expect(n.number == 0); }; "nested object partial read, don't read garbage"_test = [] { NestedPartialRead n{}; std::string buf = R"({"method":"m1","header":{"id":"51e2affb","type":"message_type","unknown key":"value",garbage},"number":51})"; expect(not glz::read(n, buf)); expect(n.method == "m1"); expect(n.header.id == "51e2affb"); expect(n.header.type == "message_type"); expect(n.number == 0); }; }; ``` glaze-4.4.3/docs/partial-write.md000066400000000000000000000016121475520231000166610ustar00rootroot00000000000000# Partial Write Glaze supports partial object writing by providing an array of JSON pointers of the fields that should be written. ```c++ struct animals_t { std::string lion = "Lion"; std::string tiger = "Tiger"; std::string panda = "Panda"; struct glaze { using T = animals_t; static constexpr auto value = glz::object(&T::lion, &T::tiger, &T::panda); }; }; struct zoo_t { animals_t animals{}; std::string name{"My Awesome Zoo"}; struct glaze { using T = zoo_t; static constexpr auto value = glz::object(&T::animals, &T::name); }; }; "partial write"_test = [] { static constexpr auto partial = glz::json_ptrs("/name", "/animals/tiger"); zoo_t obj{}; std::string s{}; const auto ec = glz::write_json(obj, s); expect(!ec); expect(s == R"({"animals":{"tiger":"Tiger"},"name":"My Awesome Zoo"})") << s; }; ``` glaze-4.4.3/docs/pure-reflection.md000066400000000000000000000037211475520231000172030ustar00rootroot00000000000000# Pure Reflection Glaze supports pure reflection for [aggregate initializable](https://en.cppreference.com/w/cpp/language/aggregate_initialization) structs in Clang, MSVC, and GCC. > Aggregate initializable means: > > - no user-declared constructors > - no user-declared or inherited constructors > - no private or protected direct non-static data members > - no [virtual base classes](https://en.cppreference.com/w/cpp/language/derived_class#Virtual_base_classes) There's no need to write any `glz::meta` structures or use any macros. The reflection is hidden from the user and computed at compile time. - You can still write a `glz::meta` to customize your serialization, which will override the default reflection. > CUSTOMIZATION NOTE: > > When writing custom serializers specializing `to_json` or `from_json`, your concepts for custom types might not take precedence over the reflection engine (when you haven't written a `glz::meta` for your type). The reflection engine tries to discern that no specialization occurs for your type, but this is not always possible. > > To solve this problem, you can add `static constexpr auto glaze_reflect = false;` to your class. Or, you can add a `glz::meta` for your type. ## Mixing glz::meta reflection with pure-reflected structs If you have an struct with constructors you may need to use `glz::meta` to define the reflection. But, now your type might break the counting of the numbers of members when included in another struct. To fix this, add a constructor for your struct to enable the proper counting: `my_struct(glz::make_reflectable) {}` Example: ```c++ struct V2 { float x{}; float y{}; V2(glz::make_reflectable) {} V2() = default; V2(V2&&) = default; V2(const float* arr) : x(arr[0]), y(arr[1]) {} }; template <> struct glz::meta { static constexpr auto value = object(&V2::x, &V2::y); }; struct V2Wrapper { V2 x{}; // reflection wouldn't work except for adding `V2(glz::make_reflectable) {}` }; ``` glaze-4.4.3/docs/ranges.md000066400000000000000000000014401475520231000153530ustar00rootroot00000000000000# Ranges Glaze has special support for standard ranges. You can create ranges/views of `std::pair` to write out concatenated JSON objects. ```c++ auto num_view = std::views::iota(-2, 3) | std::views::transform([](const auto i) { return std::pair(i, i * i); }); expect(glz::write_json(num_view) == glz::sv{R"({"-2":4,"-1":1,"0":0,"1":1,"2":4})"}); auto str_view = std::views::iota(-2, 3) | std::views::transform([](const auto i) { return std::pair(i, std::to_string(i * i)); }); expect(glz::write_json(str_view) == glz::sv{R"({"-2":"4","-1":"1","0":"0","1":"1","2":"4"})"}); ``` ## Arrays of Arrays If you want to write out a JSON array of two-element arrays, don't use `std::pair` as the value type. Instead, use `std::array`, `std::tuple`, or `glz::array`. glaze-4.4.3/docs/recorder.md000066400000000000000000000012251475520231000157020ustar00rootroot00000000000000# Data Recorder `record/recorder.hpp` provides an efficient recorder for mixed data types. The template argument takes all the supported types. The recorder stores the data as a variant of deques of those types. `std::deque` is used to avoid the cost of reallocating when a `std::vector` would grow, and typically a recorder is used in cases when the length is unknown. ```c++ glz::recorder rec; double x = 0.0; float y = 0.f; rec["x"] = x; rec["y"] = y; for (int i = 0; i < 100; ++i) { x += 1.5; y += static_cast(i); rec.update(); // saves the current state of x and y } glz::write_file_json(rec, "recorder_out.json"); ``` glaze-4.4.3/docs/reflection.md000066400000000000000000000030101475520231000162210ustar00rootroot00000000000000# Reflection in Glaze Glaze provides compile time reflection utilities for C++. ```c++ struct T { int a{}; std::string str{}; std::array arr{}; }; static_assert(glz::refl::size == 3); static_assert(glz::refl::keys[0] == "a"); ``` ## glz::convert_struct The `glz::convert_struct` function show a simple application of Glaze reflection at work. It allows two different structs with the same member names to convert from one to the other. If any of the fields don't have matching names, a compile time error will be generated. ```c++ struct a_type { float fluff = 1.1f; int goo = 1; std::string stub = "a"; }; struct b_type { float fluff = 2.2f; int goo = 2; std::string stub = "b"; }; struct c_type { std::optional fluff = 3.3f; std::optional goo = 3; std::optional stub = "c"; }; suite convert_tests = [] { "convert a to b"_test = [] { a_type in{}; b_type out{}; glz::convert_struct(in, out); expect(out.fluff == 1.1f); expect(out.goo == 1); expect(out.stub == "a"); }; "convert a to c"_test = [] { a_type in{}; c_type out{}; glz::convert_struct(in, out); expect(out.fluff.value() == 1.1f); expect(out.goo.value() == 1); expect(out.stub.value() == "a"); }; "convert c to a"_test = [] { c_type in{}; a_type out{}; glz::convert_struct(in, out); expect(out.fluff == 3.3f); expect(out.goo == 3); expect(out.stub == "c"); }; }; ``` glaze-4.4.3/docs/rpc/000077500000000000000000000000001475520231000143375ustar00rootroot00000000000000glaze-4.4.3/docs/rpc/json-rpc.md000066400000000000000000000065711475520231000164250ustar00rootroot00000000000000## JSON-RPC 2.0 Compile time specification of JSON-RPC methods making it unnecessary to convert JSON to its according params/result/error type. - [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification) ### Example full working example can be seen in source code [examples/json-rpc.cpp](../../examples/json-rpc.cpp) ```C++ struct foo_params { int foo_a{}; std::string foo_b{}; }; struct foo_result { bool foo_c{}; std::string foo_d{}; auto operator<=>(const foo_result&) const = default; }; struct bar_params { int bar_a; std::string bar_b; }; struct bar_result { bool bar_c{}; std::string bar_d{}; auto operator<=>(const bar_result&) const = default; }; namespace rpc = glz::rpc; int main() { rpc::server, rpc::method<"bar", bar_params, bar_result>> server; rpc::client, rpc::method<"bar", bar_params, bar_result>> client; // One long living callback per method for the server server.on<"foo">([](foo_params const& params) -> glz::expected { // access to member variables for the request `foo` // params.foo_a // params.foo_b if( params.foo_a != 0) return foo_result{.foo_c = true, .foo_d = "new world"}; else // Or return an error: return glz::unexpected{rpc::error{rpc::error_e::server_error_lower, {}, "my error"}}; }); server.on<"bar">([](bar_params const& params) { return bar_result{.bar_c = true, .bar_d = "new world"}; }); std::string uuid{"42"}; // One callback per client request auto [request_str, inserted] = client.request<"foo">( uuid, foo_params{.foo_a = 1337, .foo_b = "hello world"}, [](glz::expected value, rpc::id_t id) -> void { // Access to value/error and/or id }); // request_str: R"({"jsonrpc":"2.0","method":"foo","params":{"foo_a":1337,"foo_b":"hello world"},"id":"42"})" // send request_str over your communication protocol to the server // you can assign timeout for the request in your event loop auto timeout = [uuid, &client]() { decltype(auto) map = client.get_request_map<"foo">(); if (map.contains(uuid)) map.erase(uuid); }; timeout(); // Call the server callback for method `foo` // Returns response json string since the request_str can withold batch of requests. // If the request is a notification (no `id` in request) a response will not be generated. // For convenience, you can serialize the response yourself and get the responses as following: // auto response_vector = server.call("..."); // std::string response = glz::write_json(response_vector); std::string response = server.call(request_str); assert(response == R"({"jsonrpc":"2.0","result":{"foo_c":true,"foo_d":"new world"},"id":"42"})"); // Call the client callback for method `foo` with the provided results // This will automatically remove the previously assigned callback client.call(response); // This would return an internal error since the `id` is still not in the request map auto err = client.call(response); } ``` > Thanks to **[jbbjarnason](https://github.com/jbbjarnason)** glaze-4.4.3/docs/rpc/repe-rpc.md000066400000000000000000000034311475520231000163770ustar00rootroot00000000000000# REPE RPC > [!WARNING] > > **The `glz::asio_server`, `glz::asio_client`, and `glz::repe::registry`, are all under active development and should not be considered stable.** Glaze provides support for the [REPE](https://github.com/stephenberry/repe) RPC format along with client/server implementations (currently [asio](http://think-async.com/Asio/)). - [REPE specification](https://github.com/stephenberry/repe) JSON Pointer syntax is used to refer to fields in C++ structures. ## REPE Registry The `glz::repe::registry` produces an API for function invocation and variable access for server implementations. > The registry does not currently support adding methods from RPC calls or adding methods once RPC calls can be made. ## REPE registry locking and thread safety The `glz::repe::registry` allows users to expose C++ classes directly to the registry as an interface for network RPC calls and POST/GET calls. > [!IMPORTANT] > > Like most registry implementations, no locks are acquired for reading/writing to the registry and all thread safety must be managed by the user. This allows flexible, performant interfaces to be developed on top of the registry. It is recommended to register safe types, such as `std::atomic`. Glaze provides some higher level thread safe classes to be used in these asynchronous interfaces. ## Thread Safe Classes - `glz::async_string` in `glaze/thread/async_string.hpp` - Provides a thread safe `std::string` wrapper, which Glaze can read/write from safely in asynchronous calls. ## asio > [!NOTE] > > `glz::asio_server` and `glz::asio_client` require the [standalone asio](https://think-async.com/Asio/AsioStandalone.html) to build. Glaze does not include this dependency within its CMake files. The developer is expected to include this header-only library. glaze-4.4.3/docs/stencil.md000066400000000000000000000045061475520231000155430ustar00rootroot00000000000000# Stencil (String Interpolation) > [!WARNING] > > glz::stencil is still somewhat experimental. The API may not be as stable as other parts of Glaze. Glaze provides string interpolation for C++ structs. The `stencil` feature provides a templating mechanism for formatting structured data into strings. Inspired by the Mustache templating language, it enables the generation of dynamic output by combining predefined templates with C++ structs. Example: ```c++ struct person { std::string first_name{}; std::string last_name{}; uint32_t age{}; bool hungry{}; }; "person"_test = [] { std::string_view layout = R"({{first_name}} | {{last_name}} = {{age}})"; person p{"Henry", "Foster", 34}; auto result = glz::stencil(layout, p); expect(result == "Henry | Foster = 34"); }; "section_with_inner_placeholders"_test = [] { std::string_view layout = R"({{first_name}} {{last_name}} {{#employed}}Status: Employed, Age: {{age}}{{/employed}})"; person p{"Carol", "Davis", 30, true, true}; auto result = glz::stencil(layout, p); expect(result == "Carol Davis Status: Employed, Age: 30"); }; "inverted_section_true"_test = [] { std::string_view layout = R"({{first_name}} {{last_name}} {{^hungry}}I'm not hungry{{/hungry}})"; person p{"Henry", "Foster", 34, false}; // hungry is false auto result = glz::stencil(layout, p); expect(result == "Henry Foster I'm not hungry"); }; ``` > [!NOTE] > > `result` in these examples is a `std::expected`. Like most functions in Glaze (e.g. `glz::write_json`) you can also pass in your own string buffer as the last argument, in which case the return type is `glz::error_ctx`. ## Specification - `{{key}}` - Represents an interpolated field, to be replaced. - `{{#boolean_key}} SECTION {{/boolean_key}}` - Activates the SECTION if the boolean field is true. - `{{^boolean_key}} SECTION {{/boolean_key}}` - Activates the SECTION if the boolean field is false. - `{{! Here is a comment}}` - Comments are skipped when interpolating. ## Why Not Full Mustache Specification? At some point Glaze may introduce a `glz::mustache` function with full support. But, `glz::stencil` attempts to provide a more efficient solution by limiting the specification and not escaping HTML characters. HTML escaping could always be added as a compile time option in the future. glaze-4.4.3/docs/stl-support.md000066400000000000000000000027341475520231000164170ustar00rootroot00000000000000# Standard Template Library Support Glaze uses C++20 concepts to not only support the standard template library, but also other libraries or custom types that conform to the same interface. ## Array Types Array types logically convert to JSON array values. Concepts are used to allow various containers and even user containers if they match standard library interfaces. - `std::tuple` - `std::array` - `std::vector` - `std::deque` - `std::list` - `std::forward_list` - `std::span` - `std::set` - `std::unordered_set` ## Object Types - `glz::object` (compile time mixed types) - `std::map` - `std::unordered_map` ## Variants - `std::variant` See [Variant Handling](./docs/variant-handling.md) for more information. ## Nullable Types - `std::unique_ptr` - `std::shared_ptr` - `std::optional` Nullable types may be allocated by valid input or nullified by the `null` keyword. ## String Types - `std::string` - `std::string_view` - `std::filesystem::path` - Note that `std::filesystem::path` does not work with pure reflection on GCC and MSVC (requires a glz::meta) - `std::bitset` ```c++ std::bitset<8> b = 0b10101010; // will be serialized as a string: "10101010" ``` ## std::expected `std::expected` is supported where the expected value is treated as it would typically be handled in JSON, and the unexpected value is treated as a JSON object with the unexpected value referred to by an `"unexpected"` key. Example of unexpected: ```json {"unexpected": "my error string"} ``` glaze-4.4.3/docs/thread-pool.md000066400000000000000000000022471475520231000163200ustar00rootroot00000000000000# Thread Pool Glaze contains a thread pool for the sake of running studies efficiently across threads using the JSON study code. However, the thread pool is generic and can be used for various applications. It is designed to minimize copies of the data passed to threads. ```c++ glz::pool pool(4); // creates a thread pool that will run on 4 threads std::atomic x = 0; auto f = [&] { ++x; }; // some worker function for (auto i = 0; i < 1000; ++i) { pool.emplace_back(f); // run jobs in parallel } pool.wait(); // wait until all jobs are completed expect(x == 1000); ``` The thread pool holds a queue of jobs, which are executed in parallel up to the number of threads designated by the pool's constructor. If the number of threads is not specified then it uses all threads available. The example below shows how results can be returned from a worker function and stored with `std::future`. ```c++ glz::pool pool; auto f = [] { std::mt19937_64 generator{}; std::uniform_int_distribution dist{ 0, 100 }; return dist(generator); }; std::vector> numbers; for (auto i = 0; i < 1000; ++i) { numbers.emplace_back(pool.emplace_back(f)); } ``` # glaze-4.4.3/docs/threading/000077500000000000000000000000001475520231000155205ustar00rootroot00000000000000glaze-4.4.3/docs/threading/shared-async-map.md000066400000000000000000000123041475520231000211760ustar00rootroot00000000000000# glz::shared_async_map ## Features - **Thread Safety:** Automatically manages synchronization, allowing safe concurrent insertions, deletions, and accesses. - **Flat Map Structure:** Utilizes a sorted `std::vector` for storage, ensuring cache-friendly access patterns and efficient lookups via binary search. - **Iterator Support:** Provides both mutable and immutable iterators with built-in synchronization. - **Value Proxies:** Offers proxy objects to manage access to values, ensuring that locks are held appropriately during value manipulation. - **Avoids Manual Mutex Management:** Developers do not need to explicitly handle mutexes, reducing the risk of synchronization bugs. ## Why Use `shared_async_map`? In multi-threaded applications, managing shared data structures often requires explicit synchronization using mutexes. This can be error-prone and cumbersome, leading to potential deadlocks, race conditions, and other concurrency issues. `glz::shared_async_map` abstracts away the complexity of mutex management by internally handling synchronization, allowing developers to focus on the core logic of their applications. ### Benefits Over Manual Mutex Management 1. **Simplified Code:** Eliminates the need for boilerplate mutex locking and unlocking, resulting in cleaner and more readable code. 2. **Reduced Risk of Errors:** Minimizes the chances of common synchronization mistakes such as forgetting to unlock a mutex or locking in the wrong order. 3. **Automatic Lock Handling:** Ensures that appropriate locks are held during operations, maintaining data integrity without developer intervention. 4. **Optimized Performance:** Uses `std::shared_mutex` to allow multiple concurrent readers, improving performance in read-heavy scenarios. ### Additional Benefits - **Cache Efficiency:** The use of a sorted `std::vector` ensures better cache locality compared to node-based structures like `std::map`. - **Memory Overhead:** Lower memory footprint due to the contiguous storage of elements. - **Flexible Access Patterns:** Supports both direct access via `operator[]` and range-based iteration with iterators. - **Exception Safety:** Provides strong exception guarantees, ensuring that the internal state remains consistent even in the presence of exceptions. ## Implementation Details ### Internal Structure - **Storage:** Elements are stored as `std::unique_ptr>` within a sorted `std::vector`. The sorting is based on the keys, enabling efficient binary search operations. - **Synchronization:** A `std::shared_mutex` guards the internal vector, allowing multiple threads to read concurrently while ensuring exclusive access for modifications. ### Key Components 1. **Shared State (`shared_state`):** - Contains the `std::vector` of elements and the `std::shared_mutex`. - Shared among all instances of the map via `std::shared_ptr`. 2. **Iterators (`iterator` and `const_iterator`):** - Provide access to the elements with built-in synchronization. - Acquire shared locks to ensure thread-safe traversal. 3. **Value Proxies (`value_proxy` and `const_value_proxy`):** - Manage access to the values, holding necessary locks to ensure safe manipulation. ### Thread Safety Mechanisms - **Read Operations:** - Utilize `std::shared_lock` to allow multiple concurrent readers. - Methods like `find`, `at`, `count`, and `contains` acquire shared locks. - **Write Operations:** - Utilize `std::unique_lock` to ensure exclusive access during modifications. - Methods like `insert`, `emplace`, `erase`, and `clear` acquire unique locks. ## Internal Operations ### Binary Search for Key Lookup The `shared_async_map` maintains its elements in a sorted `std::vector`, enabling efficient binary search operations for key lookup. This ensures that operations like `find`, `insert`, and `erase` have logarithmic time complexity (`O(log n)`). ### Lock Management - **Shared Locks (`std::shared_lock`):** Acquired for read-only operations, allowing multiple threads to read concurrently. - **Unique Locks (`std::unique_lock`):** Acquired for write operations, ensuring exclusive access to modify the map. ### Iterator Locking Iterators hold shared locks to guarantee that the underlying data remains consistent during traversal. When an iterator is dereferenced or incremented, the lock is maintained to prevent data races and ensure thread safety. ## Exception Safety All public member functions of `shared_async_map` provide strong exception safety guarantees. In the event of an exception (e.g., during memory allocation or key comparisons), the internal state of the map remains consistent, and no resources are leaked. ## Limitations - **Element Type Requirements:** The value type `V` must be thread-safe, as `shared_async_map` assumes that concurrent access to individual elements does not require additional synchronization. - **Performance Overhead:** While `shared_async_map` optimizes for concurrent access, the use of shared locks and dynamic memory allocations (via `std::unique_ptr`) may introduce some performance overhead compared to non-thread-safe containers. - **Insertion Order:** The map maintains elements in sorted order based on keys, which may not be suitable for scenarios where insertion order needs to be preserved. glaze-4.4.3/docs/time-trace.md000066400000000000000000000046041475520231000161330ustar00rootroot00000000000000# Time Trace and Profiling Use Google's [Perfetto](https://ui.perfetto.dev/) to visualize time traces, enabling simple profiling of your C++. Glaze writes out JSON traces conformant to Google's [Trace Event Format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview). Glaze provides `begin` and `end` members for duration traces. Asynchronous traces use `async_begin` and `async_end`. An example of the API in use: ```c++ #include "glaze/trace/trace.hpp" glz::trace trace{}; // make a structure to store a trace trace.begin("my event name") // compute something here trace.end("my event name") auto ec = glz::write_file_json(trace, "trace.json", std::string{}); ``` ## Args (metadata) Glaze allows `args` as an additional argument to `begin` and `end` methods, which will provide metadata in [Perfetto](https://ui.perfetto.dev/). ```c++ // arguments could be any type that can be serialized to JSON trace.begin("my event name", arguments); // do stuff trace.end("my event name"); ``` Example with a string: ```c++ trace.begin("my event name", "My event description"); // do stuff trace.end("my event name"); ``` ## Disabling trace ```c++ trace.disabled = true; // no data will be collected ``` ## Global tracing It can be bothersome to pass around a `glz::trace` instance. A global instance is available with a separate API to avoid needing to pass a trace instance around. `glz::disable_trace()` - Turns off the global tracing. `glz::enable_trace()` - Turns on the global tracing if it had been disabled (enabled by default). `glz::trace_begin("my event")` - Add a begin event for "my event" `glz::trace_end("my event")` - Add an end event for "my event" `glz::trace_async_begin("my event")` - Add an async_begin event for "my event" `glz::trace_async_end("my event")` - Add an aysnc_end event for "my event" Two structs are also provided that will automatically add the end events when they leave scope: `glz::duration_trace` `glz::async_trace` Example: ```c++ void my_function_to_profile() { glz::duration_trace trace{"my function to profile"}; // do stuff here // glz::duration_trace will automatically add the end event when it goes out of scope. } ``` Then, somewhere at the end of your program, write your global trace to file: ```c++ const auto ec = glz::write_file_trace("trace.json", std::string{}); if (ec) { std::cerr << "trace output failed\n"; } ``` glaze-4.4.3/docs/unknown-keys.md000066400000000000000000000047321475520231000165530ustar00rootroot00000000000000# Unknown Keys Sometimes you don't know in advance all the keys of your objects. By default, glaze will error on unknown keys. If you set the compile time option `error_on_unknown_keys = false`, the behavior will change to skip the unknown keys. Furthermore it is possible to customize the handling of object unknown keys at read and/or write time by defining `unknown_read` and `unknown_write` members of meta class as pointer to member or pointer to method. Note : `unknown_write` is using `glz::merge` internally so unknown keys will be written at the end of object. ## Example ```c++ struct my_struct { std::string hello; std::string zzz; std::map extra; // store key/raw_json in extra map // with methods // void my_unknown_read(const glz::sv& key, const glz::raw_json& value) { // extra[key] = value; // }; // std::map my_unknown_write() const { // return extra; // } }; template <> struct glz::meta { using T = my_struct; static constexpr auto value = object( "hello", &T::hello, "zzz", &T::zzz ); // with members static constexpr auto unknown_write{&T::extra}; static constexpr auto unknown_read{&T::extra}; // with methods // static constexpr auto unknown_write{&T::my_unknown_write}; // static constexpr auto unknown_read{&T::my_unknown_read}; }; // usage std::string buffer = R"({"hello":"Hello World!","lang":"en","zzz":"zzz"})"; my_struct s{}; // decodes and retains extra unknown fields (lang) in extra map glz::context ctx{}; glz::read(s, buffer, ctx); // update s.hello = "Hi !" // encodes output string std::string out{}; glz::write_json(s, out); // out == {"hello":"Hi !","zzz":"zzz","lang":"en"} ``` ## Example 2 : Known extra type If the type of extra keys is known, this would work ```c++ struct my_struct { std::string infinite; int zero; std::map numbers; }; template <> struct glz::meta { using T = my_struct; static constexpr auto value = object( "infinite", &T::infinite, "zero", &T::zero ); static constexpr auto unknown_read{&T::extra}; }; // usage // note extra keys (one, two) are of type int std::string buffer = R"({"inf":"infinite","zero":0,"one":1,"two":2})"; my_struct s{}; // decodes and retains extra unknown fields (lang) in extra map glz::context ctx{}; glz::read(s, buffer, ctx); ``` glaze-4.4.3/docs/variant-handling.md000066400000000000000000000101401475520231000173170ustar00rootroot00000000000000# Variant Handling Glaze has full support `std::variant` when writing, and read support when either the type of the underlying JSON is deducible or the current type stored in the variant is the correct type. ## Basic Types Types can be auto-deduced if the variant contains at most one type matching each of the fundamental JSON types of [string, number, object, array boolean] or multiple object types. `std::variant` could be auto deduced but `std::variant` cannot be. Write example: ```c++ std::variant d = "not_a_fish"; auto s = glz::write_json(d); expect(s == R"("not_a_fish")"); ``` Read example: ```c++ std::variant x = 44; glz::read_json(x, "33"); expect(std::get(x) == 33); ``` ## Object Types ### Auto Deduction of Object Types Objects can be auto deduced based on the presence of unique key combinations. ```c++ struct xy_t { int x{}; int y{}; }; template <> struct glz::meta { using T = xy_t; static constexpr auto value = object("x", &T::x, "y", &T::y); }; struct yz_t { int y{}; int z{}; }; template <> struct glz::meta { using T = yz_t; static constexpr auto value = object("y", &T::y, "z", &T::z); }; struct xz_t { int x{}; int z{}; }; template <> struct glz::meta { using T = xz_t; static constexpr auto value = object("x", &T::x, "z", &T::z); }; suite metaobject_variant_auto_deduction = [] { "metaobject_variant_auto_deduction"_test = [] { std::variant var{}; std::string b = R"({"y":1,"z":2})"; expect(glz::read_json(var, b) == glz::error_code::none); expect(std::holds_alternative(var)); expect(std::get(var).y == 1); expect(std::get(var).z == 2); b = R"({"x":5,"y":7})"; expect(glz::read_json(var, b) == glz::error_code::none); expect(std::holds_alternative(var)); expect(std::get(var).x == 5); expect(std::get(var).y == 7); b = R"({"z":3,"x":4})"; expect(glz::read_json(var, b) == glz::error_code::none); expect(std::holds_alternative(var)); expect(std::get(var).z == 3); expect(std::get(var).x == 4); }; }; ``` ### Deduction of Tagged Object Types If you don't want auto deduction, or if you need to deduce the type based on the value associated with a key, Glaze supports custom tags. ```c++ struct put_action { std::map data{}; }; template <> struct glz::meta { using T = put_action; static constexpr auto value = object("data", &T::data); }; struct delete_action { std::string data{}; }; template <> struct glz::meta { using T = delete_action; static constexpr auto value = object("data", &T::data); }; using tagged_variant = std::variant; template <> struct glz::meta { static constexpr std::string_view tag = "action"; static constexpr auto ids = std::array{"PUT", "DELETE"}; //Defaults to glz::name_v of the type if ids is not supplied }; suite tagged_variant_tests = [] { "tagged_variant_write_tests"_test = [] { tagged_variant var = delete_action{{"the_internet"}}; std::string s{}; glz::write_json(var, s); expect(s == R"({"action":"DELETE","data":"the_internet"})"); }; "tagged_variant_read_tests"_test = [] { tagged_variant var{}; expect(glz::read_json(var, R"({"action":"DELETE","data":"the_internet"})") == glz::error_code::none); expect(std::get(var).data == "the_internet"); }; }; ``` ## BEVE to JSON BEVE uses the variant index to denote the type in a variant. When calling `glz::beve_to_json`, variants will be written in JSON with `"index"` and `"value"` keys. The index indicates the type, which would correspond to a `std::variant` `index()` method. ```json { "index": 1, "value": "my value" } ``` > BEVE conversion to JSON does not support `string` tags, to simplify the specification and avoid bifurcation of variant handling. Using the index is more efficient in binary and more directly translated to `std::variant`. glaze-4.4.3/docs/volatile-support.md000066400000000000000000000016621475520231000174330ustar00rootroot00000000000000# Volatile Support Glaze supports the `volatile` qualifier, which is especially useful when interacting with hardware registers. Glaze also provides `glz::volatile_array` in `glaze/hardware/volatile_array.hpp`. This class provides the same API as `std::array`, but with volatile memory and methods. Example struct definition using automatic reflection: ```c++ struct my_struct { glz::volatile_array a{}; bool b{}; int32_t c{}; double d{}; uint32_t e{}; }; ``` Example unit tests: ```c++ volatile my_struct obj{{1, 2, 3, 4}, true, -7, 9.9, 12}; std::string s{}; glz::write_json(obj, s); expect(s == R"({"a":[1,2,3,4],"b":true,"c":-7,"d":9.9,"e":12})") << s; obj.a.fill(0); obj.b = false; obj.c = 0; obj.d = 0.0; obj.e = 0; expect(!glz::read_json(obj, s)); expect(obj.a == glz::volatile_array{1, 2, 3, 4}); expect(obj.b == true); expect(obj.c == -7); expect(obj.d == 9.9); expect(obj.e == 12); ``` glaze-4.4.3/docs/wrappers.md000066400000000000000000000340501475520231000157420ustar00rootroot00000000000000# Wrappers Glaze provides a number of wrappers that indicate at compile time how a value should be read and/or written. These wrappers allow you to modify the read/write behavior of a type without affecting your C++ class. ## Available Wrappers ```c++ glz::append_arrays<&T::x> // When reading into an array that is appendable, the new data will be appended rather than overwrite glz::bools_as_numbers<&T::x> // Read and write booleans as numbers glz::quoted_num<&T::x> // Read and write numbers as strings glz::quoted<&T::x> // Read a value as a string and unescape, to avoid the user having to parse twice glz::number<&T::x> // Read a string as a number and writes the string as a number glz::raw<&T::x> // Write out string like types without quotes glz::raw_string<&T::string> // Do not decode/encode escaped characters for strings (improves read/write performance) glz::escaped<&T::string> // Opposite of glz::raw_string, it turns off this behavior glz::partial_read<&T::x> // Reads into only existing fields and elements and then exits without parsing the rest of the input glz::invoke<&T::func> // Invoke a std::function, lambda, or member function with n-arguments as an array input glz::write_float32<&T::x> // Writes out numbers with a maximum precision of float32_t glz::write_float64<&T::x> // Writes out numbers with a maximum precision of float64_t glz::write_float_full<&T::x> // Writes out numbers with full precision (turns off higher level float precision wrappers) glz::custom<&T::read, &T::write> // Calls custom read and write std::functions, lambdas, or member functions glz::manage<&T::x, &T::read_x, &T::write_x> // Calls read_x() after reading x and calls write_x() before writing x ``` ## Associated glz::opts `glz::opts` is the compile time options struct passed to most of Glaze functions to configure read/write behavior. Often wrappers are associated with compile time options and can also be set via `glz::opts`. For example, the `glz::quoted_num` wrapper is associated with the `quoted_num` boolean in `glz::opts`. ## append_arrays When reading into an array that is appendable, the new data will be appended rather than overwrite Associated option: `glz::opts{.append_arrays = true};` ```c++ struct append_obj { std::vector names{}; std::vector> arrays{}; }; template <> struct glz::meta { using T = append_obj; static constexpr auto value = object("names", append_arrays<&T::names>, "arrays", append_arrays<&T::arrays>); }; ``` In use: ```c++ append_obj obj{}; expect(not glz::read_json(obj, R"({"names":["Bob"],"arrays":[[0,0]]})")); expect(obj.names == std::vector{"Bob"}); expect(obj.arrays == std::vector>{{0,0}}); expect(not glz::read_json(obj, R"({"names":["Liz"],"arrays":[[1,1]]})")); expect(obj.names == std::vector{"Bob", "Liz"}); expect(obj.arrays == std::vector>{{0,0},{1,1}}); ``` ## bools_as_numbers Read and write booleans as numbers Associated option: `glz::opts{.bools_as_numbers = true};` ```c++ struct bools_as_numbers_struct { bool a{}; bool b{}; bool c{}; bool d{}; struct glaze { using T = bools_as_numbers_struct; static constexpr auto value = glz::object("a", glz::bools_as_numbers<&T::a>, "b", glz::bools_as_numbers<&T::b>, &T::c, &T::d); }; }; ``` In use: ```c++ std::string s = R"({"a":1,"b":0,"c":true,"d":false})"; bools_as_numbers_struct obj{}; expect(!glz::read_json(obj, s)); expect(obj.a == true); expect(obj.b == false); expect(glz::write_json(obj) == s); ``` ### bools_as_numbers from glz::opts You don't have to use wrappers if you want the global behavior to handle booleans as numbers. ```c++ std::string s = R"([1,0,1,0])"; std::array obj{}; constexpr glz::opts opts{.bools_as_numbers = true}; expect(!glz::read(obj, s)); expect(glz::write(obj) == s); ``` ## quoted_num Read and write numbers as strings. Associated option: `glz::opts{.quoted_num = true};` ```c++ struct foo { int x{}; }; template <> struct glz::meta { using T = foo; static constexpr auto value = object("x", quoted_num<&T::x>); }; ``` In use: ```c++ std::string input = R"({ "x": "5" })"; foo obj{}; expect(!glz::read_json(obj, input)); expect(glz::write_json(obj) == R"({ "x": "5" })"); ``` > `quoted_num` is more efficient than `quoted` for numbers. ## quoted When reading, first reads a value as a string, which unescapes, and then reads the value normally. When writing, will first write the value as a string and then write the string to produce escapes. > `glz::quoted` is useful for storing escaped JSON inside of a higher level JSON object. ```c++ struct client_state { uint64_t id{}; std::map> layouts{}; }; template <> struct glz::meta { using T = client_state; static constexpr auto value = object("id", &T::id, "layouts", quoted<&T::layouts>); }; ``` In use: ```c++ client_state obj{}; std::string input = R"({ "id": 4848, "layouts": "{\"first layout\": [ \"inner1\", \"inner2\" ] }" })"; expect(!glz::read_json(obj, input)); expect(obj.id == 4848); expect(obj.layouts.at("first layout") == std::vector{"inner1", "inner2"}); std::string out{}; glz::write_json(obj, out); expect(out == R"({"id":4848,"layouts":"{\"first layout\":[\"inner1\",\"inner2\"]}"})"); ``` ## number Read JSON numbers into strings and write strings as JSON numbers. Associated option: `glz::opts{.number = true};` ```c++ struct numbers_as_strings { std::string x{}; std::string y{}; }; template <> struct glz::meta { using T = numbers_as_strings; static constexpr auto value = object("x", glz::number<&T::x>, "y", glz::number<&T::y>); }; ``` In use: ```c++ std::string input = R"({"x":555,"y":3.14})"; numbers_as_strings obj{}; expect(!glz::read_json(obj, input)); expect(obj.x == "555"); expect(obj.y == "3.14"); std::string output; glz::write_json(obj, output); expect(input == output); ``` ## raw Write out string like types without quotes. > Useful for when a string is already in JSON format and doesn't need to be quoted. Associated option: `glz::opts{.raw = true};` ```c++ struct raw_struct { std::string str{}; }; template <> struct glz::meta { using T = raw_struct; static constexpr auto value = object("str", glz::raw<&T::str>); }; ``` In use: ```c++ suite raw_test = [] { raw_struct obj{.str = R"("Hello")"}; // quotes would have been escaped if str were not wrapped with raw expect(glz::write_json(obj) == R"({"str":"Hello"})"); }; ``` ## raw_string Do not decode/encode escaped characters for strings (improves read/write performance). > If your code does not care about decoding escaped characters or you know your input will never have escaped characters, this wrapper makes reading/writing that string faster. Associated option: `glz::opts{.raw_string = true};` ```c++ struct raw_stuff { std::string a{}; std::string b{}; std::string c{}; struct glaze { using T = raw_stuff; static constexpr auto value = glz::object(&T::a, &T::b, &T::c); }; }; struct raw_stuff_wrapper { raw_stuff data{}; struct glaze { using T = raw_stuff_wrapper; static constexpr auto value{glz::raw_string<&T::data>}; }; }; ``` In use: ```c++ raw_stuff_wrapper obj{}; std::string buffer = R"({"a":"Hello\nWorld","b":"Hello World","c":"\tHello\bWorld"})"; expect(!glz::read_json(obj, buffer)); expect(obj.data.a == R"(Hello\nWorld)"); expect(obj.data.b == R"(Hello World)"); expect(obj.data.c == R"(\tHello\bWorld)"); buffer.clear(); glz::write_json(obj, buffer); expect(buffer == R"({"a":"Hello\nWorld","b":"Hello World","c":"\tHello\bWorld"})"); ``` ## escaped The `glz::escaped` wrapper turns off the effects of `glz::raw_string`. ```c++ struct raw_stuff_escaped { raw_stuff data{}; struct glaze { using T = raw_stuff_escaped; static constexpr auto value{glz::escaped<&T::data>}; }; }; ``` In use: ```c++ raw_stuff_escaped obj{}; std::string buffer = R"({"a":"Hello\nWorld"})"; expect(!glz::read_json(obj, buffer)); expect(obj.data.a == R"(Hello World)"); buffer.clear(); glz::write_json(obj, buffer); expect(buffer == R"({"a":"Hello\nWorld","b":"","c":""})"); ``` ## partial_read Reads into existing object and array elements and then exits without parsing the rest of the input. More documentation concerning `partial_read` can be found in the [Partial Read documentation](./partial-read.md). > `partial_read` is useful when parsing header information before deciding how to decode the rest of a document. Or, when you only care about the first few elements of an array. ## invoke Invoke a std::function or member function with n-arguments as an array input. ```c++ struct invoke_struct { int y{}; std::function square{}; void add_one() { ++y; } // MSVC requires this constructor for 'this' to be captured invoke_struct() { square = [&](int x) { y = x * x; }; } }; template <> struct glz::meta { using T = invoke_struct; static constexpr auto value = object("square", invoke<&T::square>, "add_one", invoke<&T::add_one>); }; ``` In use: ```c++ std::string s = R"( { "square":[5], "add_one":[] })"; invoke_struct obj{}; expect(!glz::read_json(obj, s)); expect(obj.y == 26); // 5 * 5 + 1 }; ``` ## write_float32 Writes out numbers with a maximum precision of `float32_t`. ```c++ struct write_precision_t { double pi = std::numbers::pi_v; struct glaze { using T = write_precision_t; static constexpr auto value = glz::object("pi", glz::write_float32<&T::pi>); }; }; ``` In use: ```c++ write_precision_t obj{}; std::string json_float = glz::write_json(obj); expect(json_float == R"({"pi":3.1415927})") << json_float; ``` ## write_float64 Writes out numbers with a maximum precision of `float64_t`. ## write_float_full Writes out numbers with full precision (turns off higher level float precision wrappers). ## Associated glz::opts for float precision ```c++ enum struct float_precision : uint8_t { full, float32 = 4, float64 = 8, float128 = 16 }; ``` glz::opts ```c++ // The maximum precision type used for writing floats, higher precision floats will be cast down to this precision float_precision float_max_write_precision{}; ``` ## custom Calls custom read and write std::functions, lambdas, or member functions. ```c++ struct custom_encoding { uint64_t x{}; std::string y{}; std::array z{}; void read_x(const std::string& s) { x = std::stoi(s); } uint64_t write_x() { return x; } void read_y(const std::string& s) { y = "hello" + s; } auto& write_z() { z[0] = 5; return z; } }; template <> struct glz::meta { using T = custom_encoding; static constexpr auto value = object("x", custom<&T::read_x, &T::write_x>, // "y", custom<&T::read_y, &T::y>, // "z", custom<&T::z, &T::write_z>); }; ``` In use: ```c++ "custom_reading"_test = [] { custom_encoding obj{}; std::string s = R"({"x":"3","y":"world","z":[1,2,3]})"; expect(!glz::read_json(obj, s)); expect(obj.x == 3); expect(obj.y == "helloworld"); expect(obj.z == std::array{1, 2, 3}); }; "custom_writing"_test = [] { custom_encoding obj{}; std::string s = R"({"x":"3","y":"world","z":[1,2,3]})"; expect(!glz::read_json(obj, s)); std::string out{}; glz::write_json(obj, out); expect(out == R"({"x":3,"y":"helloworld","z":[5,2,3]})"); }; ``` ### Another custom example Showing use of constexpr lambdas for customization. ```c++ struct custom_buffer_input { std::string str{}; }; template <> struct glz::meta { static constexpr auto read_x = [](custom_buffer_input& s, const std::string& input) { s.str = input; }; static constexpr auto write_x = [](auto& s) -> auto& { return s.str; }; static constexpr auto value = glz::object("str", glz::custom); }; ``` In use: ```c++ std::string s = R"({"str":"Hello!"})"; custom_buffer_input obj{}; expect(!glz::read_json(obj, s)); expect(obj.str == "Hello!"); s.clear(); glz::write_json(obj, s); expect(s == R"({"str":"Hello!"})"); expect(obj.str == "Hello!"); ``` > [!NOTE] > > With read lambdas like `[](custom_buffer_input& s, const std::string& input)`, both types must be concrete (cannot use `auto`), otherwise you'll get a compilation error noting this. The reason is that Glaze must be able to determine what type to decode into before passing the decoded value to `input`. ## manage Calls a read function after reading and calls a write function before writing. > `glz::manage` is useful for transforming state from a user facing format into a more complex or esoteric internal format. ```c++ struct manage_x { std::vector x{}; std::vector y{}; bool read_x() { y = x; return true; } bool write_x() { x = y; return true; } }; template <> struct glz::meta { using T = manage_x; static constexpr auto value = object("x", manage<&T::x, &T::read_x, &T::write_x>); }; ``` In use: ```c++ manage_x obj{}; std::string s = R"({"x":[1,2,3]})"; expect(!glz::read_json(obj, s)); expect(obj.y[0] == 1); expect(obj.y[1] == 2); obj.x.clear(); s.clear(); glz::write_json(obj, s); expect(s == R"({"x":[1,2,3]})"); expect(obj.x[0] == 1); expect(obj.x[1] == 2); ``` ### Another manage example ```c++ struct manage_x_lambda { std::vector x{}; std::vector y{}; }; template <> struct glz::meta { using T = manage_x_lambda; static constexpr auto read_x = [](auto& s) { s.y = s.x; return true; }; static constexpr auto write_x = [](auto& s) { s.x = s.y; return true; }; [[maybe_unused]] static constexpr auto value = object("x", manage<&T::x, read_x, write_x>); }; ``` In use: ```c++ manage_x_lambda obj{}; std::string s = R"({"x":[1,2,3]})"; expect(!glz::read_json(obj, s)); expect(obj.y[0] == 1); expect(obj.y[1] == 2); obj.x.clear(); s.clear(); glz::write_json(obj, s); expect(s == R"({"x":[1,2,3]})"); expect(obj.x[0] == 1); expect(obj.x[1] == 2); ``` glaze-4.4.3/examples/000077500000000000000000000000001475520231000144415ustar00rootroot00000000000000glaze-4.4.3/examples/CMakeLists.txt000066400000000000000000000002501475520231000171760ustar00rootroot00000000000000project(jsonrpc_example) add_executable(json-rpc EXCLUDE_FROM_ALL) target_sources(json-rpc PRIVATE json-rpc.cpp) target_link_libraries( json-rpc PRIVATE glaze::glaze) glaze-4.4.3/examples/json-rpc.cpp000066400000000000000000000100321475520231000166740ustar00rootroot00000000000000#include #include struct foo_params { int foo_a{}; std::string foo_b{}; }; struct foo_result { bool foo_c{}; std::string foo_d{}; auto operator<=>(const foo_result&) const = default; }; struct bar_params { int bar_a; std::string bar_b; }; struct bar_result { bool bar_c{}; std::string bar_d{}; auto operator<=>(const bar_result&) const = default; }; namespace rpc = glz::rpc; int main() { rpc::server, rpc::method<"bar", bar_params, bar_result>> server; rpc::client, rpc::method<"bar", bar_params, bar_result>> client; // One long living callback per method for the server server.on<"foo">([](const foo_params& params) -> glz::expected { // access to member variables for the request `foo` // params.foo_a // params.foo_b if (params.foo_a == 1337) { std::cout << "Server received valid data:" << params.foo_b << std::endl; return foo_result{.foo_c = true, .foo_d = "new world"}; } else { std::cout << "Server received invalid data:" << params.foo_b << std::endl; // Or return an error: return glz::unexpected{glz::rpc::error{glz::rpc::error_e::invalid_params, {}, "foo_a should be equal to 0"}}; } }); server.on<"bar">([](const bar_params& params) { return bar_result{.bar_c = true, .bar_d = "new world"}; }); std::string uuid{"42"}; auto client_cb = [](glz::expected value, rpc::id_t id) -> void { if (value) std::cout << "Client received " << value.value().foo_c << ":" << value.value().foo_d << std::endl; else std::cerr << "Client received error " << value.error().message << std::endl; }; // One callback per client request auto [request_str, inserted] = client.request<"foo">(uuid, foo_params{.foo_a = 1337, .foo_b = "hello world"}, client_cb); // request_str: R"({"jsonrpc":"2.0","method":"foo","params":{"foo_a":1337,"foo_b":"hello world"},"id":"42"})" // send request_str over your communication protocol to the server // you can assign timeout for the request in your event loop auto timeout = [uuid, &client]() { decltype(auto) map = client.get_request_map<"foo">(); if (map.contains(uuid)) map.erase(uuid); }; #if 0 // caling timeout would cause to not process response timeout(); #endif // Call the server callback for method `foo` // Returns response json string since the request_str can withold batch of requests. // If the request is a notification (no `id` in request) a response will not be generated. // For convenience, you can serialize the response yourself and get the responses as following: // auto response_vector = server.call("..."); // std::string response = glz::write_json(response_vector); std::string response = server.call(request_str); std::cout << "Server json response :" << response << std::endl; assert(response == R"({"jsonrpc":"2.0","result":{"foo_c":true,"foo_d":"new world"},"id":"42"})"); // Call the client callback for method `foo` with the provided results // This will automatically remove the previously assigned callback auto err = client.call(response); std::cout << "Client call result :" << err.message << std::endl; // This would return an internal error since the `id` is still not in the request map err = client.call(response); std::cout << "Client retry call result :" << err.message << std::endl; std::tie(request_str, inserted) = client.request<"foo">(uuid, foo_params{.foo_a = -1, .foo_b = "invalid data"}, client_cb); response = server.call(request_str); std::cout << "Server json response :" << response << std::endl; assert(response == R"({"jsonrpc":"2.0","error":{"code":-32602,"message":"foo_a should be equal to 0"},"id":"42"})"); err = client.call(response); std::cout << "Client call with error result :" << err.message << std::endl; } glaze-4.4.3/fuzzing/000077500000000000000000000000001475520231000143175ustar00rootroot00000000000000glaze-4.4.3/fuzzing/CMakeLists.txt000066400000000000000000000052621475520231000170640ustar00rootroot00000000000000 # this library provides main() for compilers that do not have libfuzzer (everyone except clang) # so the source code is compiled regardless of compiler, which prevents code rot. # also, the library provides a way to invoke the fuzzer on external data which is # useful to run the fuzz corpus through code also when not using clang. add_library(main STATIC main.cpp) target_compile_features(main PRIVATE cxx_std_23) # see if libfuzzer is supported - this gets more complicated than just checking # for clang, because apple clang does not seem to support it and windows also # support clang nowadays (and perhaps libfuzzer) include(CheckSourceCompiles) set(CMAKE_REQUIRED_LINK_OPTIONS -fsanitize=fuzzer) check_source_compiles(CXX " #include #include extern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { return 0;} " haslibfuzzer) macro(create_fuzztest testname) add_executable(fuzz_${testname} ${testname}.cpp) target_link_libraries(fuzz_${testname} PRIVATE glaze::glaze) if(haslibfuzzer) target_compile_options(fuzz_${testname} PUBLIC -fsanitize=fuzzer) target_link_options(fuzz_${testname} PUBLIC -fsanitize=fuzzer) else() # supply a separate main target_link_libraries(fuzz_${testname} PRIVATE main) endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") target_compile_options(fuzz_${testname} PUBLIC -fsanitize=address,undefined -fno-sanitize-recover=all ) target_link_options(fuzz_${testname} PUBLIC -fsanitize=address,undefined -fno-sanitize-recover=all ) else() target_link_libraries(fuzz_${testname} PRIVATE main) endif() endmacro() create_fuzztest(binary_reflection) create_fuzztest(json_generic) create_fuzztest(json_minify) create_fuzztest(json_prettify) create_fuzztest(json_reflection) create_fuzztest(json_roundtrip_floating) create_fuzztest(json_roundtrip_int) create_fuzztest(json_roundtrip_string) create_fuzztest(json_with_comments) macro(create_exhaustive_test testname) add_executable(${testname} ${testname}.cpp) target_link_libraries(${testname} PRIVATE glaze::glaze) # do not set sanitizers here - CI needs to run as fast as possible, # manual running may want sanitizers and debug. if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") #target_compile_options(${testname} PRIVATE -O2) #target_compile_options(${testname} PRIVATE -fsanitize=address,undefined -fno-sanitize-recover=all ) #target_link_options(${testname} PRIVATE -fsanitize=address,undefined -fno-sanitize-recover=all ) endif() endmacro() create_exhaustive_test(json_exhaustive_roundtrip_int) create_exhaustive_test(json_exhaustive_roundtrip_float) glaze-4.4.3/fuzzing/binary_reflection.cpp000066400000000000000000000016251475520231000205250ustar00rootroot00000000000000#include #include #include #include #include struct my_struct { int i = 287; double d = 3.14; std::string hello = "Hello World"; std::array arr = {1, 2, 3}; }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { // use a vector with null termination instead of a std::string to avoid // small string optimization to hide bounds problems std::vector buffer{Data, Data + Size}; // non-null terminated { const auto& input = buffer; [[maybe_unused]] auto s = glz::read_beve(input); std::string json_output{}; [[maybe_unused]] auto ec = glz::beve_to_json(input, json_output); } // null terminated { buffer.push_back('\0'); const auto& input = buffer; [[maybe_unused]] auto s = glz::read_beve(input); } return 0; } glaze-4.4.3/fuzzing/build_and_run_fuzzers.sh000077500000000000000000000012661475520231000212600ustar00rootroot00000000000000#!/bin/sh # # builds and runs the fuzzers for a short while. # # intended to be run manually during development. gitlab CI has it's own # script. # set -eu SCRIPTDIR=$(dirname "$0") builddir=build-fuzzer-clang cmake -B "$builddir" -S "$SCRIPTDIR/.." -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=Off -GNinja -DCMAKE_CXX_COMPILER=/usr/bin/clang++-17 cmake --build "$builddir" cd "$builddir" mkdir -p corpus fuzzers=$(ls fuzzing/fuzz_*|sort --random-sort) export UBSAN_OPTIONS=halt_on_error=1 export ASAN_OPTS=halt_on_error=1 for fuzzer in $fuzzers ; do echo looking at $fuzzer shortname=$(basename $fuzzer) mkdir -p corpus/$shortname $fuzzer -max_total_time=60 corpus/$shortname done glaze-4.4.3/fuzzing/json_exhaustive_roundtrip_float.cpp000066400000000000000000000045441475520231000235430ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include // must be outside test(), compilation fails on gcc 13 otherwise struct Value { // use a short name to reduce the number of characters written float v; }; void test() { using UT = std::uint32_t; static_assert(sizeof(float) == sizeof(UT)); auto test_one_value = [](const UT loopvar, auto& outbuf) { Value s; std::memcpy(&s.v, &loopvar, sizeof(float)); if (!std::isfinite(s.v)) { return; } outbuf.clear(); const auto writeec = glz::write_json(s, outbuf); if (writeec) [[unlikely]] { std::cerr << "failed writing " << s.v << " to json\n"; std::abort(); } auto restored = glz::read_json(outbuf); if (!restored) [[unlikely]] { std::cerr << "failed parsing " << outbuf << '\n'; std::abort(); } if (const auto r = restored.value().v; r != s.v) [[unlikely]] { std::cerr << "failed roundtrip, got " << r << " instead of " << s.v << // " (diff is " << r - s.v << ") when parsing " << outbuf << '\n'; std::abort(); } }; auto test_all_in_range = [&](const UT start, const UT stop) { std::string outbuf; for (UT i = start; i < stop; ++i) { test_one_value(i, outbuf); } }; const auto nthreads = std::thread::hardware_concurrency(); const UT step = std::numeric_limits::max() / nthreads; std::vector threads; threads.reserve(nthreads); for (int threadi = 0; threadi < nthreads; ++threadi) { const UT start = threadi * step; const UT stop = (threadi == nthreads - 1) ? std::numeric_limits::max() : start + step; // std::cout << "thread i=" << threadi << " goes from " << start << " to " << stop << '\n'; threads.emplace_back(test_all_in_range, start, stop); } // test the last value here. { std::string buf; test_one_value(std::numeric_limits::max(), buf); } std::cout << "started testing in " << nthreads << " threads." << std::endl; for (auto& t : threads) { t.join(); } std::cout << "tested " << std::numeric_limits::max() << " values of float" << std::endl; } int main(int argc, char* argv[]) { test(); } glaze-4.4.3/fuzzing/json_exhaustive_roundtrip_int.cpp000066400000000000000000000051461475520231000232270ustar00rootroot00000000000000#include #include #include #include #include #include #include #include // must be outside test(), compilation fails on gcc 13 otherwise template struct Value { T value{}; }; template void test() { using S = Value; using UT = std::make_unsigned_t; auto testone = [](const UT loopvar, auto& outbuf) { S s; std::memcpy(&s.value, &loopvar, sizeof(T)); outbuf.clear(); const auto writeec = glz::write_json(s, outbuf); if (writeec) [[unlikely]] { std::cerr << "failed writing " << s.value << " to json\n"; std::abort(); } auto restored = glz::read_json(outbuf); if (!restored) [[unlikely]] { std::cerr << "failed parsing " << outbuf << '\n'; std::abort(); } if (const auto r = restored.value().value; r != s.value) [[unlikely]] { std::cerr << "failed roundtrip, got " << r << " instead of " << s.value << // " (diff is " << r - s.value << ") when parsing " << outbuf << '\n'; std::abort(); } }; auto testrange = [&](const UT start, const UT stop) { std::string outbuf; for (UT i = start; i < stop; ++i) { testone(i, outbuf); } }; const auto nthreads = std::thread::hardware_concurrency(); const UT step = std::numeric_limits::max() / nthreads; // can't use jthread, does not exist in all stdlibs. std::vector threads; threads.reserve(nthreads); for (int threadi = 0; threadi < nthreads; ++threadi) { const UT start = threadi * step; const UT stop = (threadi == nthreads - 1) ? std::numeric_limits::max() : start + step; // std::cout << "thread i=" << threadi << " goes from " << start << " to " << stop << '\n'; threads.emplace_back(testrange, start, stop); } // test the last value here. { std::string buf; testone(std::numeric_limits::max(), buf); } std::cout << "started testing in " << nthreads << " threads." << std::endl; for (auto& t : threads) { t.join(); } std::cout << "tested " << std::numeric_limits::max() << " values of " << (std::is_unsigned_v ? "un" : "") << "signed type of size " << sizeof(T) << std::endl; } void testonetype(int bits) { switch (bits) { case 16: test(); test(); break; case 32: test(); test(); break; } } int main(int argc, char* argv[]) { testonetype(16); testonetype(32); } glaze-4.4.3/fuzzing/json_generic.cpp000066400000000000000000000021071475520231000174700ustar00rootroot00000000000000#include #include #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { // non-null terminated { const std::vector buffer{Data, Data + Size}; static constexpr glz::opts opts{.null_terminated = false}; glz::json_t json{}; auto ec = glz::read(json, buffer); if (!ec) { [[maybe_unused]] auto s = json.size(); } } // use a vector with null termination instead of a std::string to avoid // small string optimization to hide bounds problems std::vector buffer{Data, Data + Size}; buffer.push_back('\0'); // const qualified input buffer { const auto& input = buffer; glz::json_t json{}; auto ec = glz::read_json(json, input); if (!ec) { [[maybe_unused]] auto s = json.size(); } } // non-const input buffer { glz::json_t json{}; auto ec = glz::read_json(json, buffer); if (!ec) { [[maybe_unused]] auto s = json.size(); } } return 0; } glaze-4.4.3/fuzzing/json_minify.cpp000066400000000000000000000011211475520231000173420ustar00rootroot00000000000000#include #include #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { // use a vector with null termination instead of a std::string to avoid // small string optimization to hide bounds problems std::vector buffer{Data, Data + Size}; // non-null terminated { [[maybe_unused]] auto maybe_smaller = glz::minify_json(buffer); } // null terminated { buffer.push_back('\0'); [[maybe_unused]] auto maybe_smaller = glz::minify_json(buffer); } return 0; } glaze-4.4.3/fuzzing/json_prettify.cpp000066400000000000000000000012171475520231000177230ustar00rootroot00000000000000#include #include #include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { // use a vector with null termination instead of a std::string to avoid // small string optimization to hide bounds problems std::vector buffer{Data, Data + Size}; // non-null terminated { const auto& input = buffer; [[maybe_unused]] auto beautiful = glz::prettify_json(input); } // null terminated { buffer.push_back('\0'); const auto& input = buffer; [[maybe_unused]] auto beautiful = glz::prettify_json(input); } return 0; } glaze-4.4.3/fuzzing/json_reflection.cpp000066400000000000000000000016361475520231000202140ustar00rootroot00000000000000#include #include #include #include #include struct my_struct { int i = 287; double d = 3.14; std::string hello = "Hello World"; std::array arr = {1, 2, 3}; }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { // use a vector with null termination instead of a std::string to avoid // small string optimization to hide bounds problems std::vector buffer{Data, Data + Size}; // non-null terminated { my_struct obj{}; [[maybe_unused]] auto ec = glz::read(obj, std::string_view{buffer.data(), Size}); } // null terminated { buffer.push_back('\0'); [[maybe_unused]] auto s = glz::read_json(std::string_view{buffer.data(), Size}); if (s) { // hooray! valid json found } } return 0; } glaze-4.4.3/fuzzing/json_roundtrip_floating.cpp000066400000000000000000000017651475520231000217760ustar00rootroot00000000000000#include #include #include #include #include #include #include // must be outside test() to work in gcc<14 template struct Value { T value{}; }; template void test(const uint8_t* Data, size_t Size) { using S = Value; S s{}; if (Size >= sizeof(T)) { std::memcpy(&s.value, Data, sizeof(T)); } else { return; } if (std::isfinite(s.value)) { auto str = glz::write_json(s).value_or(std::string{}); auto restored = glz::read_json(str); assert(restored); assert(restored.value().value == s.value); } } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { if (Size < 1 + sizeof(float)) { return 0; } const auto action = Data[0]; ++Data; --Size; switch (action & 0b1) { case 0: test(Data, Size); break; case 1: test(Data, Size); break; } return 0; } glaze-4.4.3/fuzzing/json_roundtrip_int.cpp000066400000000000000000000022621475520231000207560ustar00rootroot00000000000000#include #include #include #include #include #include // must be outside test() to work in gcc<14 template struct Value { T value{}; }; template void test(const uint8_t* Data, size_t Size) { using S = Value; S s{}; if (Size >= sizeof(T)) { std::memcpy(&s.value, Data, sizeof(T)); } else { return; } auto str = glz::write_json(s).value_or(std::string{}); auto restored = glz::read_json(str); assert(restored); assert(restored.value().value == s.value); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { if (Size < 2) { return 0; } const auto action = Data[0]; ++Data; --Size; switch (action & 0b11) { case 0: test(Data, Size); test(Data, Size); break; case 1: test(Data, Size); test(Data, Size); break; case 2: test(Data, Size); test(Data, Size); break; case 3: test(Data, Size); test(Data, Size); break; } return 0; } glaze-4.4.3/fuzzing/json_roundtrip_string.cpp000066400000000000000000000015141475520231000214710ustar00rootroot00000000000000#include #include #include #include #include #include #include struct S { std::string value{}; }; void test(const uint8_t* Data, size_t Size) { S s{{Data, Data + Size}}; // note - glaze does not escape control characters. see https://github.com/stephenberry/glaze/issues/812 // replace control characters with space for (auto& c : s.value) { if (std::iscntrl(c) && c != '\b' && c != '\f' && c != '\n' && c != '\r' && c != '\t') { c = ' '; } } auto str = glz::write_json(s).value_or(std::string{}); auto restored = glz::read_json(str); assert(restored); assert(restored.value().value == s.value); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { test(Data, Size); return 0; } glaze-4.4.3/fuzzing/json_with_comments.cpp000066400000000000000000000012551475520231000207370ustar00rootroot00000000000000#include #include #include #include #include struct my_struct { int i = 287; double d = 3.14; std::string hello = "Hello World"; std::array arr = {1, 2, 3}; }; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { // use a vector with null termination instead of a std::string to avoid // small string optimization to hide bounds problems std::vector buffer{Data, Data + Size}; buffer.push_back('\0'); [[maybe_unused]] auto s = glz::read_jsonc(std::string_view{buffer.data(), Size}); if (s) { // hooray! valid json found } return 0; } glaze-4.4.3/fuzzing/main.cpp000066400000000000000000000040521475520231000157500ustar00rootroot00000000000000#include #include #include #include #include #include /* * this provides a way to run garbage data through glaze even on platforms * that do not have libFuzzer. data can be taken from a fuzzing session * and feeding it to a program with this main function. */ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size); void handle_file(std::filesystem::path file) { std::vector data; const auto Nbytes = file_size(file); data.resize(Nbytes); std::ifstream filestream(file); filestream.read(data.data(), data.size()); if (filestream.gcount() != Nbytes) { std::cerr << "failed reading from file " << file << '\n'; return; } std::cout << "invoking fuzzer on data from file " << file << '\n'; LLVMFuzzerTestOneInput(reinterpret_cast(data.data()), data.size()); } void handle_directory(std::filesystem::path directory) { for (const auto& entry : std::filesystem::recursive_directory_iterator( directory, std::filesystem::directory_options::follow_directory_symlink)) { if (entry.is_regular_file()) { handle_file(entry.path()); } else if (entry.is_symlink()) { auto resolved = read_symlink(entry); if (is_regular_file(resolved)) { handle_file(resolved); } } } } void handle_possible_file(std::filesystem::path possible_file) { if (std::filesystem::is_directory(possible_file)) { handle_directory(possible_file); } else if (is_regular_file(possible_file)) { handle_file(possible_file); } else if (is_symlink(possible_file)) { auto resolved = read_symlink(possible_file); handle_possible_file(resolved); } else { std::cerr << "not a directory, regular file or symlink: " << possible_file << '\n'; } } void handle_arg(const char* path) { return handle_possible_file(std::filesystem::path(path)); } int main(int argc, char* argv[]) { for (int i = 1; i < argc; ++i) { handle_arg(argv[i]); } } glaze-4.4.3/fuzzing/minimize_and_cleanse.sh000077500000000000000000000007501475520231000210150ustar00rootroot00000000000000#!/bin/sh # # minimizes and cleanses a crash. # arg1 is the fuzzer # arg2 is the crash case usage() { echo "$0 fuzzer crashcase" } if [ $# -ne 2 ] ; then usage exit 1 fi if [ ! -x "$1" ] ; then echo "fuzzer should be passes as arg 1" exit 1 fi if [ ! -e "$2" ] ; then echo "crash case should be passes as arg 2" exit 1 fi "$1" "$2" -minimize_crash=1 -exact_artifact_path=minimized_crash -max_total_time=30 "$1" minimized_crash -cleanse_crash=1 -exact_artifact_path=cleaned_crash glaze-4.4.3/fuzzing/ossfuzz.sh000077500000000000000000000007011475520231000163770ustar00rootroot00000000000000#!/bin/sh # # this is the entry point for build.sh on oss fuzz, so that changes in how to # build the fuzzers does not require changes in oss fuzz but instead can # be done in the glaze repository. set -eux $CXX --version for SRCFILE in $(ls fuzzing/*.cpp |grep -v -E '(exhaustive|main\.cpp)'); do NAME=$(basename $SRCFILE .cpp) $CXX $CXXFLAGS -std=c++23 -g -Iinclude \ $SRCFILE -o $OUT/$NAME \ $LIB_FUZZING_ENGINE done glaze-4.4.3/fuzzing/quickfuzz/000077500000000000000000000000001475520231000163525ustar00rootroot00000000000000glaze-4.4.3/fuzzing/quickfuzz/run_all.sh000077500000000000000000000003661475520231000203520ustar00rootroot00000000000000#!/bin/sh # # invoked from github action. # runs all fuzzers using all cores for a limited time set -eu SCRIPTDIR=$(dirname "$0") ls fuzzing/fuzz_*|sort --random-sort | \ xargs --verbose --max-procs $(nproc) -n 1 "$SCRIPTDIR"/run_single.sh glaze-4.4.3/fuzzing/quickfuzz/run_single.sh000077500000000000000000000013161475520231000210570ustar00rootroot00000000000000#!/bin/sh # # invoked from run_all.sh # # runs a single fuzzer a limited time. # if no problems are found, the results are removed. # problems are found, logs and artifacts are left and the exit # status is nonzero. set -eu fuzzer="$1" if [ ! -x "$fuzzer" ] ; then exit 1 fi shortname=$(basename "$fuzzer") ARTIFACTS=artifacts/$shortname CORPUS=$ARTIFACTS/corpus LOG=$ARTIFACTS/$shortname.log mkdir -p $ARTIFACTS $CORPUS export UBSAN_OPTIONS=halt_on_error=1 export ASAN_OPTS=halt_on_error=1 if ! $fuzzer -max_total_time=40 -artifact_prefix=$ARTIFACTS/ $CORPUS >$LOG 2>&1 ; then echo FAIL - fuzzer $shortname failed - see $LOG exit 1 fi echo OK - fuzzer $shortname ran without problems rm -rf $ARTIFACTS/ glaze-4.4.3/include/000077500000000000000000000000001475520231000142465ustar00rootroot00000000000000glaze-4.4.3/include/glaze/000077500000000000000000000000001475520231000153505ustar00rootroot00000000000000glaze-4.4.3/include/glaze/api/000077500000000000000000000000001475520231000161215ustar00rootroot00000000000000glaze-4.4.3/include/glaze/api/api.hpp000066400000000000000000000112471475520231000174100ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include #include #include #include #include "glaze/api/std/string.hpp" #include "glaze/api/trait.hpp" #include "glaze/core/context.hpp" #include "glaze/core/opts.hpp" #include "glaze/util/expected.hpp" namespace glz { inline namespace v0_0_3 { template using func_return_t = std::conditional_t, std::reference_wrapper>, R>; struct api { api() noexcept = default; api(const api&) noexcept = default; api(api&&) noexcept = default; api& operator=(const api&) noexcept = default; api& operator=(api&&) noexcept = default; virtual ~api() noexcept {} template [[nodiscard]] T* get(const sv path) noexcept; // Get a std::function from a member function or std::function across the API template [[nodiscard]] expected get_fn(const sv path) noexcept; template expected, error_code> call(const sv path, Args&&... args) noexcept; [[nodiscard]] virtual bool contains(const sv path) noexcept = 0; virtual bool read(const uint32_t /*format*/, const sv /*path*/, const sv /*data*/) noexcept = 0; virtual bool write(const uint32_t /*format*/, const sv /*path*/, std::string& /*data*/) noexcept = 0; [[nodiscard]] virtual const sv last_error() const noexcept { return error; } /// unchecked `void*` access for low level programming (prefer templated get) [[nodiscard]] virtual std::pair get(const sv path) noexcept = 0; protected: virtual bool caller(const sv path, const glz::hash_t type_hash, void*& ret, std::span args) noexcept = 0; virtual std::unique_ptr get_fn(const sv path, const glz::hash_t type_hash) noexcept = 0; std::string error{}; }; using iface = std::map()>, std::less<>>; template T* api::get(const sv path) noexcept { static constexpr auto hash = glz::hash(); auto p = get(path); if (p.first && p.second == hash) { return static_cast(p.first); } return nullptr; } template glz::expected api::get_fn(const sv path) noexcept { static constexpr auto hash = glz::hash(); auto d = get_fn(path, hash); if (d) { T copy = *static_cast(d.get()); return copy; } else { return glz::unexpected(error_code::invalid_get_fn); } } template glz::expected, error_code> api::call(const sv path, Args&&... args) noexcept { using F = std::function; static constexpr auto hash = glz::hash(); static constexpr auto N = sizeof...(Args); std::array arguments; auto tuple_args = std::forward_as_tuple(std::forward(args)...); for_each([&](auto I) { std::get(arguments) = &std::get(tuple_args); }); if constexpr (std::is_pointer_v) { void* ptr{}; const auto success = caller(path, hash, ptr, arguments); if (success) { return static_cast(ptr); } } else if constexpr (std::is_void_v) { void* ptr = nullptr; const auto success = caller(path, hash, ptr, arguments); if (success) { return expected{}; } } else if constexpr (std::is_lvalue_reference_v) { void* ptr{}; const auto success = caller(path, hash, ptr, arguments); if (success) { return std::ref(*static_cast*>(ptr)); } } else { Ret value{}; void* ptr = &value; const auto success = caller(path, hash, ptr, arguments); if (success) { return value; } } return glz::unexpected(error_code::invalid_call); } using iface_fn = std::shared_ptr (*)(); } } #if defined(_WIN32) || defined(__CYGWIN__) #define DLL_EXPORT __declspec(dllexport) #else #define DLL_EXPORT #endif extern "C" DLL_EXPORT glz::iface_fn glz_iface() noexcept; glaze-4.4.3/include/glaze/api/hash.hpp000066400000000000000000000103021475520231000175510ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/api/xxh64.hpp" #include "glaze/core/meta.hpp" // Collision calculations done with the formula: e^((-k * (k - 1)/(2 * N))) // The approximation error tends to zero as N increases, and we are dealing with a large N // https://preshing.com/20110504/hash-collision-probabilities/ // With 10,000 registered types the probabilities of a collsion are: // 64 bit hash: 2.71027645e-12 === 1 - 1/e^(6249375/2305843009213693952) // 128 bit hash: 1.469221e-31 === 1 - 1/e^(6249375/42535295865117307932921825928971026432) // 256 bit hash: 4.317652e-70 === 1 - // 1/e^(6249375/14474011154664524427946373126085988481658748083205070504932198000989141204992) Probability of winning // the Mega Millions lottery: 1 / 302,575,350 === 3.30496189e-9 Number of times winning to equal a collision chance: 64 // bit: 1220 times 128 bit: 2.2494655e22 times (or 22 sextillion times) 256 bit: 7.6545351e60 times From these // calculations it is apparent that a 128 bit hash is more than sufficient namespace glz { template consteval auto make_array() { return [](std::index_sequence) { return std::array{static_cast(((Value >> (CHAR_BIT * I)) & 0xff))...}; }(std::make_index_sequence{}); } namespace detail { // convert an integer to a string_view at compile time constexpr uint64_t num_digits(auto x) noexcept // number of digits needed, including minus sign { return x < 10 ? 1 : 1 + num_digits(x / 10); } template struct metastring { const char data[sizeof...(args)] = {args...}; }; // recursive number-printing template, general case (for three or more digits) template struct numeric_builder { using type = typename numeric_builder::type; }; // special case for two digits; minus sign is handled here template struct numeric_builder<2, x, args...> { using type = metastring<'0' + x / 10, '0' + x % 10, args...>; }; // special case for one digit (positive numbers only) template struct numeric_builder<1, x, args...> { using type = metastring<'0' + x, args...>; }; // convenience wrapper for numeric_builder template struct numeric_string { // generate a unique string type representing this number using type = typename numeric_builder::type; // declare a static string of that type (instantiated later at file scope) static constexpr type value{}; static constexpr const std::string_view get() { return {value.data, num_digits(x)}; } }; } template struct int_to_sv { static constexpr auto arr = make_array(); static consteval std::string_view get() { return {arr.data(), arr.size()}; } }; template inline constexpr std::string_view int_to_sv_v = int_to_sv{}.get(); template inline consteval auto to_sv() { return detail::numeric_string::get(); } template struct hash128_i { static constexpr sv str = to_sv(); static constexpr sv h0 = int_to_sv_v; static constexpr sv h1 = int_to_sv_v; static constexpr sv value = join_v; }; template inline constexpr std::string_view hash128_i_v = hash128_i::value; template struct hash128 { static constexpr sv h0 = int_to_sv_v; static constexpr sv h1 = int_to_sv_v; static constexpr sv value = join_v; }; template constexpr std::string_view hash128_v = hash128::value; } glaze-4.4.3/include/glaze/api/impl.hpp000066400000000000000000000263461475520231000176060ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include "glaze/api/api.hpp" #include "glaze/api/std/array.hpp" #include "glaze/api/std/deque.hpp" #include "glaze/api/std/functional.hpp" #include "glaze/api/std/list.hpp" #include "glaze/api/std/map.hpp" #include "glaze/api/std/optional.hpp" #include "glaze/api/std/shared_ptr.hpp" #include "glaze/api/std/string.hpp" #include "glaze/api/std/tuple.hpp" #include "glaze/api/std/unique_ptr.hpp" #include "glaze/api/std/unordered_map.hpp" #include "glaze/api/std/variant.hpp" #include "glaze/api/std/vector.hpp" #include "glaze/api/tuplet.hpp" #include "glaze/api/type_support.hpp" #include "glaze/beve/read.hpp" #include "glaze/beve/write.hpp" #include "glaze/glaze.hpp" #include "glaze/json/read.hpp" #include "glaze/json/write.hpp" namespace glz { template struct impl : api { UserType user{}; std::pair get(const sv path) noexcept override { return get_void(user, path); } [[nodiscard]] bool contains(const sv path) noexcept override { return detail::seek_impl([&](auto&&) {}, user, path); } bool read(const uint32_t format, const sv path, const sv data) noexcept override { error_ctx pe{}; bool success; if (format == JSON) { success = detail::seek_impl([&](auto&& val) { pe = glz::read(val, data); }, user, path); } else { success = detail::seek_impl([&](auto&& val) { pe = glz::read(val, data); }, user, path); } if (success) { if (pe) { return false; } return true; } return false; } bool write(const uint32_t format, const sv path, std::string& data) noexcept override { // TODO: Support write errors when seeking if (format == JSON) { return detail::seek_impl([&](auto&& val) { std::ignore = glz::write_json(val, data); }, user, path); } else { return detail::seek_impl([&](auto&& val) { std::ignore = glz::write_beve(val, data); }, user, path); } } std::unique_ptr get_fn(const sv path, const glz::hash_t type_hash) noexcept override { return get_void_fn(user, path, type_hash); } template using ref_t = typename std::conditional_t> && std::is_lvalue_reference_v, std::add_lvalue_reference_t>, std::add_rvalue_reference_t>>; template ref_t to_ref(void* t) { using V = ref_t; return static_cast(*reinterpret_cast>>(t)); } template requires std::invocable>...> decltype(auto) call_args(F&& f, Parent&& parent, [[maybe_unused]] std::span args, std::index_sequence) { return f(parent, to_ref>(args[Is])...); } bool caller(const sv path, const glz::hash_t type_hash, void*& ret, std::span args) noexcept override { auto p = parent_last_json_ptrs(path); const auto parent_ptr = p.first; const auto last_ptr = p.second; bool found = false; detail::seek_impl( [&](auto&& parent) { using P = std::decay_t; detail::seek_impl( [&](auto&& val) { using V = std::decay_t; if constexpr (std::is_member_function_pointer_v) { using Parent = typename parent_of_fn::type; if constexpr (std::same_as) { using F = typename std_function_signature_decayed_keep_non_const_ref::type; static constexpr auto h = glz::hash(); using Ret = typename return_type::type; using Tuple = typename inputs_as_tuple::type; static constexpr auto N = glz::tuple_size_v; if (h == type_hash) [[likely]] { if constexpr (std::is_void_v) { call_args(std::mem_fn(val), parent, args, std::make_index_sequence{}); } else if constexpr (std::is_pointer_v>) { ret = call_args(std::mem_fn(val), parent, args, std::make_index_sequence{}); } else if constexpr (std::is_lvalue_reference_v) { // TODO remove const cast ret = const_cast*>( &call_args(std::mem_fn(val), parent, args, std::make_index_sequence{})); } else { *static_cast(ret) = call_args(std::mem_fn(val), parent, args, std::make_index_sequence{}); } found = true; } else [[unlikely]] { error = "mismatching types"; error += ", expected: " + std::string(glz::name_v); } } else { error = "invalid parent type"; } } else { error = "caller: type is not a member function"; } }, parent, last_ptr); }, user, parent_ptr); // seek to parent if (found) { return true; } if (error.empty()) { error = "invalid path"; } return false; } protected: template decltype(auto) unwrap(T&& val) { using V = std::decay_t; if constexpr (detail::nullable_t) { if (val) { return unwrap(*val); } else { error = "Cannot unwrap null value."; return decltype(unwrap(*val)){nullptr}; } } else { return &val; } } // Get a pointer to a value at the location of a json_ptr. Will return // nullptr if value doesnt exist or is wrong type template std::pair get_void(T&& root_value, const sv json_ptr) { void* result{}; glz::hash_t type_hash{}; const auto success = detail::seek_impl( [&](auto&& val) { using V = std::decay_t; if constexpr (std::is_member_function_pointer_v) { error = "get called on member function pointer"; } else { static constexpr auto h = glz::hash(); type_hash = h; result = unwrap(val); } }, std::forward(root_value), json_ptr); if (!error.empty()) { return {nullptr, {}}; } else if (!success) { error = "invalid path"; return {nullptr, {}}; } return {result, type_hash}; } template auto get_void_fn(T& root_value, const sv json_ptr, const glz::hash_t type_hash) { std::unique_ptr result{nullptr, nullptr}; auto p = parent_last_json_ptrs(json_ptr); const auto parent_ptr = p.first; const auto last_ptr = p.second; detail::seek_impl( [&](auto&& parent) { using P = std::decay_t; detail::seek_impl( [&](auto&& val) { using V = std::decay_t; if constexpr (std::is_member_function_pointer_v) { using Parent = typename parent_of_fn::type; if constexpr (std::same_as) { using F = typename std_function_signature::type; static constexpr auto h = glz::hash(); if (h == type_hash) [[likely]] { auto* f = new F{}; *f = [=, parent = &parent](auto&&... args) -> decltype(auto) { return ((*parent).*(val))(std::forward(args)...); }; result = std::unique_ptr{ f, [](void* ptr) { delete static_cast(ptr); }}; } else [[unlikely]] { error = "mismatching types"; error += ", expected: " + std::string(glz::name_v); } } else { error = "invalid parent type"; } } else if constexpr (is_specialization_v) { static constexpr auto h = glz::hash(); if (h == type_hash) [[likely]] { result = std::unique_ptr{&val, [](void*) {}}; } else [[unlikely]] { error = "mismatching types"; error += ", expected: " + std::string(glz::name_v); } } else { error = "get_fn: type" + std::string(glz::name_v) + " is not a member function or std::function"; } }, parent, last_ptr); }, root_value, parent_ptr); // seek to parent if (error.empty() && result == nullptr) { error = "invalid path"; } return result; } }; template inline constexpr auto make_api() { return std::shared_ptr>{new impl{}, [](impl* ptr) { delete ptr; }}; } template iface_fn make_iface() { return [] { std::shared_ptr ptr{new iface{}, [](auto* ptr) { delete ptr; }}; using T = std::tuple; constexpr auto N = sizeof...(Args); for_each([&](auto I) { using V = glz::tuple_element_t; ptr->emplace(name_v, make_api); }); return ptr; }; } } glaze-4.4.3/include/glaze/api/lib.hpp000066400000000000000000000073231475520231000174050ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include #include #include "glaze/api/api.hpp" #if defined(_WIN32) || defined(__CYGWIN__) #ifndef GLAZE_API_ON_WINDOWS #define GLAZE_API_ON_WINDOWS #endif #endif #ifdef GLAZE_API_ON_WINDOWS #ifdef NOMINMAX #include #else #define NOMINMAX #include #undef NOMINMAX #endif #define SHARED_LIBRARY_EXTENSION ".dll" #define SHARED_LIBRARY_PREFIX "" #elif __APPLE__ #include #define SHARED_LIBRARY_EXTENSION ".dylib" #define SHARED_LIBRARY_PREFIX "lib" #elif __has_include() #include #define SHARED_LIBRARY_EXTENSION ".so" #define SHARED_LIBRARY_PREFIX "lib" #endif namespace glz { #ifdef GLAZE_API_ON_WINDOWS using lib_t = HINSTANCE; #else using lib_t = void*; #endif struct lib_loader final { using create = glz::iface_fn (*)(void) noexcept; iface api_map{}; std::vector loaded_libs{}; void load(const sv path) { const std::filesystem::path libpath(path); if (std::filesystem::is_directory(libpath)) { load_libs(path); } else if (libpath.extension() == SHARED_LIBRARY_EXTENSION) { load_lib(libpath.string()); } else { load_lib_by_name(libpath.string()); } } void load_libs(const sv directory) { std::filesystem::directory_entry dir(directory); for (const auto& entry : std::filesystem::directory_iterator(dir)) { if (entry.is_regular_file() && entry.path().extension() == SHARED_LIBRARY_EXTENSION) { load_lib(entry.path().string()); } } } auto& operator[](const sv lib_name) { return api_map[std::string(lib_name)]; } lib_loader() = default; lib_loader(const lib_loader&) = delete; lib_loader(lib_loader&&) = delete; lib_loader& operator=(const lib_loader&) = delete; lib_loader& operator=(lib_loader&&) = delete; lib_loader(const std::string_view directory) { load(directory); } ~lib_loader() { api_map.clear(); for (const auto& lib : loaded_libs) { #ifdef GLAZE_API_ON_WINDOWS FreeLibrary(lib); #else dlclose(lib); #endif } } private: bool load_lib(const std::string& path) noexcept { #ifdef GLAZE_API_ON_WINDOWS std::filesystem::path file_path(path); lib_t loaded_lib = LoadLibraryW(file_path.native().c_str()); #else lib_t loaded_lib = dlopen(path.c_str(), RTLD_LAZY); #endif if (loaded_lib) { loaded_libs.emplace_back(loaded_lib); #ifdef GLAZE_API_ON_WINDOWS #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" auto* ptr = reinterpret_cast(GetProcAddress(loaded_lib, "glz_iface")); #pragma GCC diagnostic pop #else auto* ptr = reinterpret_cast(GetProcAddress(loaded_lib, "glz_iface")); #endif #else auto* ptr = reinterpret_cast(dlsym(dlopen(path.c_str(), RTLD_NOW), "glz_iface")); #endif if (ptr) { std::shared_ptr shared_iface_ptr = (*ptr)()(); api_map.merge(*shared_iface_ptr); return true; } } return false; } bool load_lib_by_name(const std::string& path) { #ifdef NDEBUG static std::string suffix = ""; #else static std::string suffix = "_d"; #endif const std::filesystem::path combined_path(path + suffix + SHARED_LIBRARY_EXTENSION); return (load_lib(std::filesystem::canonical(combined_path).string())); } }; } glaze-4.4.3/include/glaze/api/std/000077500000000000000000000000001475520231000167135ustar00rootroot00000000000000glaze-4.4.3/include/glaze/api/std/array.hpp000066400000000000000000000015331475520231000205440ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/core/meta.hpp" namespace glz { namespace detail { template struct to_chars { static constexpr char value[] = {('0' + digits)..., 0}; }; template struct explode : explode {}; template struct explode<0, digits...> : to_chars {}; } template struct num_to_string : detail::explode {}; template struct meta> { static constexpr std::string_view name = join_v, name_v, chars<",">, chars::value>, chars<">">>; }; } glaze-4.4.3/include/glaze/api/std/deque.hpp000066400000000000000000000004711475520231000205310ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/core/meta.hpp" namespace glz { template struct meta> { static constexpr std::string_view name = join_v, name_v, chars<">">>; }; } glaze-4.4.3/include/glaze/api/std/functional.hpp000066400000000000000000000035761475520231000216010ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/core/common.hpp" #include "glaze/core/meta.hpp" namespace glz { namespace detail { template struct expander { static constexpr auto impl() noexcept { const auto N = glz::tuple_size_v; if constexpr (I >= N) { return Str; } else if constexpr (I == N - 1) { return expander>>, Tuple, I + 1>::value; } else { return expander>, chars<",">>, Tuple, I + 1>::value; } } static constexpr std::string_view value = impl(); }; template constexpr std::string_view expander_v = expander::value; } template concept function = is_specialization_v; template struct meta { static constexpr auto impl() noexcept { using fun = function_traits; using R = typename fun::result_type; if constexpr (fun::N == 0 && named) { return join_v, name_v, chars<"()>">>; } else if constexpr (fun::N == 0) { return chars<"std::function">; } else { return join_v, name_v, chars<"(">, detail::expander_v, typename fun::arguments>, chars<")>">>; } } static constexpr auto arr = impl(); // Give the joined string static storage static constexpr std::string_view name{arr.data(), arr.size()}; }; } glaze-4.4.3/include/glaze/api/std/list.hpp000066400000000000000000000004661475520231000204050ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/core/meta.hpp" namespace glz { template struct meta> { static constexpr std::string_view name = join_v, name_v, chars<">">>; }; } glaze-4.4.3/include/glaze/api/std/map.hpp000066400000000000000000000005641475520231000202060ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/core/meta.hpp" namespace glz { template struct meta> { static constexpr std::string_view name = join_v, name_v, chars<",">, name_v, chars<">">>; }; } glaze-4.4.3/include/glaze/api/std/optional.hpp000066400000000000000000000005021475520231000212460ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/core/meta.hpp" namespace glz { template struct meta> { static constexpr std::string_view name = join_v, name_v, chars<">">>; }; } glaze-4.4.3/include/glaze/api/std/set.hpp000066400000000000000000000004631475520231000202220ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/core/meta.hpp" namespace glz { template struct meta> { static constexpr std::string_view name = join_v, name_v, chars<">">>; }; } glaze-4.4.3/include/glaze/api/std/shared_ptr.hpp000066400000000000000000000005041475520231000215560ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/core/meta.hpp" namespace glz { template struct meta> { static constexpr std::string_view name = join_v, name_v, chars<">">>; }; } glaze-4.4.3/include/glaze/api/std/span.hpp000066400000000000000000000013751475520231000203730ustar00rootroot00000000000000// Glaze Library // For the license information refer to glaze.hpp #pragma once #include #include "glaze/api/hash.hpp" #include "glaze/core/meta.hpp" namespace glz { template class> inline constexpr bool is_span_v = false; template