aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkuwa <kuwa.com3@gmail.com>2026-03-15 16:32:50 +0900
committerGitHub <noreply@github.com>2026-03-15 02:32:50 -0500
commitf483074cd2ff2cc9e9c3ef1df4430d4a65d1fb2a (patch)
tree9cd094e38b2259ea913fecb7d6fde16046277182
parent4d200a589dd3d0a8424eaef6c0d6fd454d16e411 (diff)
Dedicated Server Software - Minecraft.Server.exe (#498)
* add: Dedicated Server implementation - Introduced `ServerMain.cpp` for the dedicated server logic, handling command-line arguments, server initialization, and network management. - Created `postbuild_server.ps1` script for post-build tasks, including copying necessary resources and DLLs for the dedicated server. - Added `CopyServerAssets.cmake` to manage the copying of server assets during the build process, ensuring required files are available for the dedicated server. - Defined project filters in `Minecraft.Server.vcxproj.filters` for better organization of server-related files. * add: refactor world loader & add server properties - Introduced ServerLogger for logging startup steps and world I/O operations. - Implemented ServerProperties for loading and saving server configuration from `server.properties`. - Added WorldManager to handle world loading and creation based on server properties. - Updated ServerMain to integrate server properties loading and world management. - Enhanced project files to include new source and header files for the server components. * update: implement enhanced logging functionality with configurable log levels * update: update keyboard and mouse input initialization 1dc8a005ed111463c22c17b487e5ec8a3e2d30f3 * fix: change virtual screen resolution to 1920x1080(HD) Since 31881af56936aeef38ff322b975fd0 , `skinHud.swf` for 720 is not included in `MediaWindows64.arc`, the app crashes unless the virtual screen is set to HD. * fix: dedicated server build settings for miniaudio migration and missing sources - remove stale Windows64 Miles (mss64) link/copy references from server build - add Common/Filesystem/Filesystem.cpp to Minecraft.Server.vcxproj - add Windows64/PostProcesser.cpp to Minecraft.Server.vcxproj - fix unresolved externals (PostProcesser::*, FileExists) in dedicated server build * update: changed the virtual screen to 720p Since the crash caused by the 720p `skinHud.swf` not being included in `MediaWindows64.arc` has been resolved, switching back to 720p to reduce resource usage. * add: add Docker support for Dedicated Server add with entrypoint and build scripts * fix: add initial save for newly created worlds in dedicated server on the server side, I fixed the behavior introduced after commit aadb511, where newly created worlds are intentionally not saved to disk immediately. * update: add basically all configuration options that are implemented in the classes to `server.properties` * update: add LAN advertising configuration for server.properties LAN-Discovery, which isn’t needed in server mode and could potentially be a security risk, has also been disabled(only server mode). * add: add implementing interactive command line using linenoise - Integrated linenoise library for line editing and completion in the server console. - Updated ServerLogger to handle external writes safely during logging. - Modified ServerMain to initialize and manage the ServerCli for command input. - The implementation is separate from everything else, so it doesn't affect anything else. - The command input section and execution section are separated into threads. * update: enhance command line completion with predictive hints Like most command line tools, it highlights predictions in gray. * add: implement `StringUtils` for string manipulation and refactor usages Unified the scattered utility functions. * fix: send DisconnectPacket on shutdown and fix Win64 recv-thread teardown race Before this change, server/host shutdown closed sockets directly in ServerConnection::stop(), which bypassed the normal disconnect flow. As a result, clients could be dropped without receiving a proper DisconnectPacket during stop/kill/world-close paths. Also, WinsockNetLayer::Shutdown() could destroy synchronization objects while host-side recv threads were still exiting, causing a crash in RecvThreadProc (access violation on world close in host mode). * fix: return client to menus when Win64 host connection drops - Add client-side host disconnect handling in CPlatformNetworkManagerStub::DoWork() for _WINDOWS64. - When in QNET_STATE_GAME_PLAY as a non-host and WinsockNetLayer::IsConnected() becomes false, trigger g_NetworkManager.HandleDisconnect(false) to enter the normal disconnect/UI flow. - Use m_bLeaveGameOnTick as a one-shot guard to prevent repeated disconnect handling while the link remains down. - Reset m_bLeaveGameOnTick on LeaveGame(), HostGame(), and JoinGame() to avoid stale state across sessions. * update: converted Japanese comments to English * add: create `Minecraft.Server` developer guide in English and Japanese * update: add note about issue * add: add `nlohmann/json` json lib * add: add FileUtils Moved file operations to `utils`. * add: Dedicated Server BAN access manager with persistent player and IP bans - add Access frontend that publishes thread-safe ban manager snapshots for dedicated server use - add BanManager storage for banned-players.json and banned-ips.json with load/save/update flows - add persistent player and IP ban checks during dedicated server connection handling - add UTF-8 BOM-safe JSON parsing and shared file helpers backed by nlohmann/json - add Unicode-safe ban file read/write and safer atomic replacement behavior on Windows - add active-ban snapshot APIs and expiry-aware filtering for expires metadata - add RAII-based dedicated access shutdown handling during server startup and teardown * update: changed file read/write operations to use `FileUtils`. - As a side effect, saving has become faster! * fix: Re-added the source that had somehow disappeared. * add: significantly improved the dedicated server logging system - add ServerLogManager to Minecraft.Server as the single entry point for dedicated-server log output - forward CMinecraftApp logger output to the server logger when running with g_Win64DedicatedServer - add named network logs for incoming, accepted, rejected, and disconnected connections - cache connection metadata by smallId so player name and remote IP remain available for disconnect logs - keep Minecraft.Client changes minimal by using lightweight hook points and handling log orchestration on the server side * fix: added the updated library source * add: add `ban` and `pardon` commands for Player and IP * fix: fix stop command shutdown process add dedicated server shutdown request handling * fix: fixed the save logic during server shutdown Removed redundant repeated saves and eliminated the risks of async writes. * update: added new sever files to Docker entrypoint * fix: replace shutdown flag with atomic variable for thread safety * update: update Dedicated Server developer guide English is machine translated. Please forgive me. * update: check for the existence of `GameHDD` and create * add: add Whitelist to Dedicated Server * refactor: clean up and refactor the code - unify duplicated implementations that were copied repeatedly - update outdated patterns to more modern ones * fix: include UI header (new update fix) * fix: fix the detection range for excessive logging `getHighestNonEmptyY()` returning `-1` occurs normally when the chunk is entirely air. The caller (`Minecraft.World/LevelChunk.cpp:2400`) normalizes `-1` to `0`. * update: add world size config to dedicated server properties * update: update README add explanation of `server.properties` & launch arguments * update: add nightly release workflow for dedicated server and client builds to Actions * fix: update name for workflow * add random seed generation * add: add Docker nightly workflow for Dedicated Server publish to GitHub Container Registry * fix: ghost player when clients disconnect out of order #4 * fix: fix 7zip option * fix: fix Docker workflow for Dedicated Server artifact handling * add: add no build Dedicated Server startup scripts and Docker Compose * update: add README for Docker Dedicated Server setup with no local build * refactor: refactor command path structure As the number of commands has increased and become harder to navigate, each command has been organized into separate folders. * update: support stream(file stdin) input mode for server CLI Support for the stream (file stdin) required when attaching a tty to a Docker container on Linux. * add: add new CLI Console Commands for Dedicated Server Most of these commands are executed using the command dispatcher implemented on the `Minecraft.World` side. When registering them with the dispatcher, the sender uses a permission-enabled configuration that treats the CLI as a player. - default game. - enchant - experience. - give - kill(currently, getting a permission error for some reason) - time - weather. - update tp & gamemode command * fix: change player map icon to random select * update: increase the player limit * add: restore the basic anti-cheat implementation and add spawn protection Added the following anti-cheat measures and add spawn protection to `server.properties`. - instant break - speed - reach * fix: fix Docker image tag --------- Co-authored-by: sylvessa <225480449+sylvessa@users.noreply.github.com>
-rw-r--r--.github/workflows/docker-nightly.yml160
-rw-r--r--.github/workflows/nightly.yml29
-rw-r--r--.gitignore9
-rw-r--r--CMakeLists.txt96
-rw-r--r--COMPILE.md34
-rw-r--r--Minecraft.Client/Common/Console_Utils.cpp36
-rw-r--r--Minecraft.Client/Common/Consoles_App.cpp117
-rw-r--r--Minecraft.Client/Common/Network/GameNetworkManager.cpp53
-rw-r--r--Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp50
-rw-r--r--Minecraft.Client/MinecraftServer.cpp10
-rw-r--r--Minecraft.Client/MinecraftServer.h3
-rw-r--r--Minecraft.Client/PendingConnection.cpp72
-rw-r--r--Minecraft.Client/PlayerConnection.cpp104
-rw-r--r--Minecraft.Client/PlayerConnection.h5
-rw-r--r--Minecraft.Client/PlayerList.cpp12
-rw-r--r--Minecraft.Client/ServerPlayerGameMode.cpp32
-rw-r--r--Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp103
-rw-r--r--Minecraft.Client/Windows64/Network/WinsockNetLayer.h1
-rw-r--r--Minecraft.Server/Access/Access.cpp460
-rw-r--r--Minecraft.Server/Access/Access.h47
-rw-r--r--Minecraft.Server/Access/BanManager.cpp631
-rw-r--r--Minecraft.Server/Access/BanManager.h106
-rw-r--r--Minecraft.Server/Access/WhitelistManager.cpp297
-rw-r--r--Minecraft.Server/Access/WhitelistManager.h64
-rw-r--r--Minecraft.Server/Common/AccessStorageUtils.h105
-rw-r--r--Minecraft.Server/Common/FileUtils.cpp146
-rw-r--r--Minecraft.Server/Common/FileUtils.h25
-rw-r--r--Minecraft.Server/Common/NetworkUtils.h29
-rw-r--r--Minecraft.Server/Common/StringUtils.cpp212
-rw-r--r--Minecraft.Server/Common/StringUtils.h23
-rw-r--r--Minecraft.Server/Console/ServerCli.cpp44
-rw-r--r--Minecraft.Server/Console/ServerCli.h50
-rw-r--r--Minecraft.Server/Console/ServerCliEngine.cpp395
-rw-r--r--Minecraft.Server/Console/ServerCliEngine.h127
-rw-r--r--Minecraft.Server/Console/ServerCliInput.cpp285
-rw-r--r--Minecraft.Server/Console/ServerCliInput.h63
-rw-r--r--Minecraft.Server/Console/ServerCliParser.cpp116
-rw-r--r--Minecraft.Server/Console/ServerCliParser.h63
-rw-r--r--Minecraft.Server/Console/ServerCliRegistry.cpp99
-rw-r--r--Minecraft.Server/Console/ServerCliRegistry.h58
-rw-r--r--Minecraft.Server/Console/commands/CommandParsing.h39
-rw-r--r--Minecraft.Server/Console/commands/IServerCliCommand.h50
-rw-r--r--Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp171
-rw-r--r--Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h19
-rw-r--r--Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp136
-rw-r--r--Minecraft.Server/Console/commands/ban-list/CliCommandBanList.h23
-rw-r--r--Minecraft.Server/Console/commands/ban/CliCommandBan.cpp145
-rw-r--r--Minecraft.Server/Console/commands/ban/CliCommandBan.h20
-rw-r--r--Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp117
-rw-r--r--Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.h16
-rw-r--r--Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp87
-rw-r--r--Minecraft.Server/Console/commands/enchant/CliCommandEnchant.h16
-rw-r--r--Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp184
-rw-r--r--Minecraft.Server/Console/commands/experience/CliCommandExperience.h17
-rw-r--r--Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp109
-rw-r--r--Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.h18
-rw-r--r--Minecraft.Server/Console/commands/give/CliCommandGive.cpp103
-rw-r--r--Minecraft.Server/Console/commands/give/CliCommandGive.h16
-rw-r--r--Minecraft.Server/Console/commands/help/CliCommandHelp.cpp46
-rw-r--r--Minecraft.Server/Console/commands/help/CliCommandHelp.h17
-rw-r--r--Minecraft.Server/Console/commands/kill/CliCommandKill.cpp64
-rw-r--r--Minecraft.Server/Console/commands/kill/CliCommandKill.h16
-rw-r--r--Minecraft.Server/Console/commands/list/CliCommandList.cpp49
-rw-r--r--Minecraft.Server/Console/commands/list/CliCommandList.h16
-rw-r--r--Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp98
-rw-r--r--Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.h19
-rw-r--r--Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp173
-rw-r--r--Minecraft.Server/Console/commands/pardon/CliCommandPardon.h19
-rw-r--r--Minecraft.Server/Console/commands/stop/CliCommandStop.cpp32
-rw-r--r--Minecraft.Server/Console/commands/stop/CliCommandStop.h16
-rw-r--r--Minecraft.Server/Console/commands/time/CliCommandTime.cpp118
-rw-r--r--Minecraft.Server/Console/commands/time/CliCommandTime.h16
-rw-r--r--Minecraft.Server/Console/commands/tp/CliCommandTp.cpp82
-rw-r--r--Minecraft.Server/Console/commands/tp/CliCommandTp.h18
-rw-r--r--Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp49
-rw-r--r--Minecraft.Server/Console/commands/weather/CliCommandWeather.h15
-rw-r--r--Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp285
-rw-r--r--Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.h17
-rw-r--r--Minecraft.Server/Minecraft.Server.vcxproj749
-rw-r--r--Minecraft.Server/Minecraft.Server.vcxproj.filters737
-rw-r--r--Minecraft.Server/ServerLogManager.cpp402
-rw-r--r--Minecraft.Server/ServerLogManager.h127
-rw-r--r--Minecraft.Server/ServerLogger.cpp258
-rw-r--r--Minecraft.Server/ServerLogger.h44
-rw-r--r--Minecraft.Server/ServerProperties.cpp930
-rw-r--r--Minecraft.Server/ServerProperties.h105
-rw-r--r--Minecraft.Server/ServerShutdown.h6
-rw-r--r--Minecraft.Server/Windows64/ServerMain.cpp718
-rw-r--r--Minecraft.Server/Windows64/postbuild_server.ps165
-rw-r--r--Minecraft.Server/WorldManager.cpp641
-rw-r--r--Minecraft.Server/WorldManager.h92
-rw-r--r--Minecraft.Server/docs/DEVELOPMENT.en.md284
-rw-r--r--Minecraft.Server/docs/DEVELOPMENT.ja.md286
-rw-r--r--Minecraft.Server/vendor/linenoise/LICENSE25
-rw-r--r--Minecraft.Server/vendor/linenoise/linenoise.c649
-rw-r--r--Minecraft.Server/vendor/linenoise/linenoise.h37
-rw-r--r--Minecraft.Server/vendor/nlohmann/LICENSE.MIT21
-rw-r--r--Minecraft.Server/vendor/nlohmann/json.hpp25526
-rw-r--r--Minecraft.World/CompressedTileStorage.cpp4
-rw-r--r--Minecraft.World/MapItemSavedData.cpp28
-rw-r--r--MinecraftConsoles.sln55
-rw-r--r--README.md108
-rw-r--r--build-start-dedicated-server.sh85
-rw-r--r--cmake/CopyServerAssets.cmake72
-rw-r--r--docker-build-dedicated-server.sh1
-rw-r--r--docker-compose.dedicated-server.ghcr.yml27
-rw-r--r--docker-compose.dedicated-server.yml31
-rw-r--r--docker/dedicated-server/Dockerfile33
-rw-r--r--docker/dedicated-server/entrypoint.sh91
-rw-r--r--start-dedicated-server.sh45
110 files changed, 38957 insertions, 147 deletions
diff --git a/.github/workflows/docker-nightly.yml b/.github/workflows/docker-nightly.yml
new file mode 100644
index 00000000..78d5256c
--- /dev/null
+++ b/.github/workflows/docker-nightly.yml
@@ -0,0 +1,160 @@
+name: Docker Nightly Dedicated Server
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - "main"
+ - 'feature/dedicated-server'
+ paths-ignore:
+ - ".gitignore"
+ - "*.md"
+ - ".github/*.md"
+
+permissions:
+ contents: read
+ packages: write
+
+concurrency:
+ group: docker-nightly-dedicated-server
+ cancel-in-progress: true
+
+jobs:
+ build-runtime:
+ name: Build Dedicated Server Runtime
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Setup msbuild
+ uses: microsoft/setup-msbuild@v2
+
+ - name: Build Dedicated Server Runtime Only
+ shell: pwsh
+ run: |
+ MSBuild.exe Minecraft.World\Minecraft.World.vcxproj /p:Configuration=Release /p:Platform=x64 /m
+ MSBuild.exe Minecraft.Server\Minecraft.Server.vcxproj /p:Configuration=Release /p:Platform=x64 /m
+
+ - name: Stage dedicated server runtime
+ shell: pwsh
+ run: |
+ $serverOut = "Minecraft.Server/x64/Minecraft.Server/Release"
+ $stage = ".artifacts/dedicated-server-runtime"
+
+ if (Test-Path $stage) {
+ Remove-Item -Path $stage -Recurse -Force
+ }
+
+ New-Item -ItemType Directory -Path (Join-Path $stage "Windows64") -Force | Out-Null
+
+ # Minimum required runtime files
+ $required = @(
+ "Minecraft.Server.exe",
+ "iggy_w64.dll",
+ "Common"
+ )
+
+ foreach ($entry in $required) {
+ $src = Join-Path $serverOut $entry
+ if (-not (Test-Path $src)) {
+ throw "Missing required runtime path: $src"
+ }
+ }
+
+ # Copy required files
+ Copy-Item -Path (Join-Path $serverOut "Minecraft.Server.exe") -Destination (Join-Path $stage "Minecraft.Server.exe") -Force
+ Copy-Item -Path (Join-Path $serverOut "iggy_w64.dll") -Destination (Join-Path $stage "iggy_w64.dll") -Force
+ Copy-Item -Path (Join-Path $serverOut "Common") -Destination (Join-Path $stage "Common") -Recurse -Force
+ if (Test-Path (Join-Path $serverOut "Windows64")) {
+ Copy-Item -Path (Join-Path $serverOut "Windows64/*") -Destination (Join-Path $stage "Windows64") -Recurse -Force
+ } else {
+ Write-Host "Windows64 directory is not present in build output; staging without it."
+ }
+
+ Get-ChildItem -Path $stage -Recurse -File | Select-Object -First 20 | ForEach-Object {
+ Write-Host "Staged: $($_.FullName)"
+ }
+
+ - name: Upload dedicated server runtime to artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: dedicated-server-runtime
+ if-no-files-found: error
+ path: |
+ .artifacts/dedicated-server-runtime/**
+
+ docker-publish:
+ name: Build and Push Docker Image
+ runs-on: ubuntu-latest
+ needs: build-runtime
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Download dedicated server runtime from artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: dedicated-server-runtime
+ path: .artifacts/runtime
+
+ - name: Prepare Docker runtime directory
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ rm -rf runtime
+ mkdir -p runtime
+ cp .artifacts/runtime/Minecraft.Server.exe runtime/Minecraft.Server.exe
+ cp .artifacts/runtime/iggy_w64.dll runtime/iggy_w64.dll
+ cp -R .artifacts/runtime/Common runtime/Common
+ mkdir -p runtime/Windows64
+ if [[ -d ".artifacts/runtime/Windows64" ]]; then
+ cp -R .artifacts/runtime/Windows64/. runtime/Windows64/
+ fi
+
+ test -f runtime/Minecraft.Server.exe
+ test -f runtime/iggy_w64.dll
+ test -d runtime/Common
+ test -d runtime/Windows64
+
+ - name: Compute image name
+ id: image
+ shell: bash
+ run: |
+ owner="$(echo "${{ vars.CONTAINER_REGISTRY_OWNER || github.repository_owner }}" | tr '[:upper:]' '[:lower:]')"
+ image_tag="nightly"
+ # if [[ "${{ github.ref }}" != "refs/heads/main" ]]; then
+ # image_tag="nightly-test"
+ # fi
+ echo "owner=$owner" >> "$GITHUB_OUTPUT"
+ echo "image=ghcr.io/$owner/minecraft-lce-dedicated-server" >> "$GITHUB_OUTPUT"
+ echo "image_tag=$image_tag" >> "$GITHUB_OUTPUT"
+
+ - name: Extract Docker metadata
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ${{ steps.image.outputs.image }}
+ tags: |
+ type=raw,value=${{ steps.image.outputs.image_tag }}
+
+ - name: Login to GHCR
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ secrets.GHCR_USERNAME || github.actor }}
+ password: ${{ secrets.GHCR_TOKEN || secrets.GITHUB_TOKEN }}
+
+ - name: Build and push image
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ file: docker/dedicated-server/Dockerfile
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+ build-args: |
+ MC_RUNTIME_DIR=runtime \ No newline at end of file
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 402f3cb6..5af23fe6 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -1,15 +1,19 @@
-name: Nightly Release
+name: Nightly Releases
on:
workflow_dispatch:
push:
branches:
- 'main'
+ - 'feature/dedicated-server'
paths-ignore:
- '.gitignore'
- '*.md'
- '.github/*.md'
+permissions:
+ contents: write
+
jobs:
build:
name: Build Windows64
@@ -28,13 +32,16 @@ jobs:
- name: Zip Build
run: 7z a -r LCEWindows64.zip ./x64/Release/*
- - name: Update release
+ - name: Zip Dedicated Server Build
+ run: 7z a -r LCEServerWindows64.zip ./x64/Minecraft.Server/Release/*
+
+ - name: Update Client release
uses: andelf/nightly-release@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: nightly
- name: Nightly Release
+ name: Nightly Client Release
body: |
Requires at least Windows 7 and DirectX 11 compatible GPU to run. Compiled with MSVC v14.44.35207 in Release mode with Whole Program Optimization, as well as `/O2 /Ot /Oi /Ob3 /GF /fp:precise`.
@@ -44,3 +51,19 @@ jobs:
LCEWindows64.zip
./x64/Release/Minecraft.Client.exe
./x64/Release/Minecraft.Client.pdb
+
+ - name: Update Dedicated Server release
+ uses: andelf/nightly-release@main
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: nightly-dedicated-server
+ name: Nightly Dedicated Server Release
+ body: |
+ Dedicated Server runtime for Windows64.
+
+ Download `LCEServerWindows64.zip` and extract it to a folder where you'd like to keep the server runtime.
+ files: |
+ LCEServerWindows64.zip
+ ./x64/Minecraft.Server/Release/Minecraft.Server.exe
+ ./x64/Minecraft.Server/Release/Minecraft.Server.pdb
diff --git a/.gitignore b/.gitignore
index 9abf7507..bb3ea8bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -423,6 +423,12 @@ Minecraft.World/x64_Debug/
Minecraft.World/Release/
Minecraft.World/x64_Release/
+Minecraft.Server/x64/
+Minecraft.Server/Debug/
+Minecraft.Server/x64_Debug/
+Minecraft.Server/Release/
+Minecraft.Server/x64_Release/
+
build/*
# Existing build output files
@@ -434,6 +440,9 @@ build/*
# Local saves
Minecraft.Client/Saves/
+tmp*/
+_server_asset_probe/
+server-data/
# Visual Studio Per-User Config
*.user
/out
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8c6eb80e..ceb15951 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -92,6 +92,91 @@ target_link_libraries(MinecraftClient PRIVATE
>
)
+set(MINECRAFT_SERVER_SOURCES ${MINECRAFT_CLIENT_SOURCES})
+list(APPEND MINECRAFT_SERVER_SOURCES
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Windows64/ServerMain.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/Access.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/BanManager.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Access/WhitelistManager.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCli.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliInput.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliParser.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliEngine.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/ServerCliRegistry.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/ban/CliCommandBan.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/help/CliCommandHelp.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/stop/CliCommandStop.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/list/CliCommandList.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/tp/CliCommandTp.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/time/CliCommandTime.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/give/CliCommandGive.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/kill/CliCommandKill.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/FileUtils.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Common/StringUtils.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/ServerLogger.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/ServerLogManager.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/ServerProperties.cpp"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/vendor/linenoise/linenoise.c"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/WorldManager.cpp"
+)
+
+add_executable(MinecraftServer ${MINECRAFT_SERVER_SOURCES})
+target_include_directories(MinecraftServer PRIVATE
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/include"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Xbox/Sentient/Include"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.World/x64headers"
+ "${CMAKE_CURRENT_SOURCE_DIR}/include/"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Server/Windows64"
+)
+target_compile_definitions(MinecraftServer PRIVATE
+ $<$<CONFIG:Debug>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD>
+ $<$<NOT:$<CONFIG:Debug>>:_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD>
+)
+if(MSVC)
+ configure_msvc_target(MinecraftServer)
+ target_link_options(MinecraftServer PRIVATE
+ $<$<CONFIG:Release>:/LTCG /INCREMENTAL:NO>
+ )
+endif()
+
+set_target_properties(MinecraftServer PROPERTIES
+ OUTPUT_NAME "Minecraft.Server"
+ VS_DEBUGGER_WORKING_DIRECTORY "$<TARGET_FILE_DIR:MinecraftServer>"
+ VS_DEBUGGER_COMMAND_ARGUMENTS "-port 25565 -bind 0.0.0.0 -name DedicatedServer"
+)
+
+target_link_libraries(MinecraftServer PRIVATE
+ MinecraftWorld
+ d3d11
+ XInput9_1_0
+ wsock32
+ legacy_stdio_definitions
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/lib/iggy_w64.lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/lib/iggyperfmon_w64.lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/Iggy/lib/iggyexpruntime_w64.lib"
+ $<$<CONFIG:Debug>:
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Input_d.lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Storage_d.lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC_d.lib"
+ >
+ $<$<NOT:$<CONFIG:Debug>>:
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Input.lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Storage.lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/Minecraft.Client/Windows64/4JLibs/libs/4J_Render_PC.lib"
+ >
+)
+
if(CMAKE_HOST_WIN32)
message(STATUS "Starting redist copy...")
execute_process(
@@ -147,4 +232,13 @@ else()
message(FATAL_ERROR "Redist and asset copying is only supported on Windows (Robocopy) and Unix systems (rsync).")
endif()
-set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MinecraftClient)
+add_custom_command(TARGET MinecraftServer POST_BUILD
+ COMMAND "${CMAKE_COMMAND}"
+ -DPROJECT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
+ -DOUTPUT_DIR="$<TARGET_FILE_DIR:MinecraftServer>"
+ -DCONFIGURATION=$<CONFIG>
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CopyServerAssets.cmake"
+ VERBATIM
+)
+
+set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT MinecraftServer)
diff --git a/COMPILE.md b/COMPILE.md
index b62c9575..86fb5cee 100644
--- a/COMPILE.md
+++ b/COMPILE.md
@@ -3,7 +3,9 @@
## Visual Studio (`.sln`)
1. Open `MinecraftConsoles.sln` in Visual Studio 2022.
-2. Set `Minecraft.Client` as the Startup Project.
+2. Set Startup Project:
+ - Client: `Minecraft.Client`
+ - Dedicated server: `Minecraft.Server`
3. Select configuration:
- `Debug` (recommended), or
- `Release`
@@ -12,6 +14,17 @@
- `Build > Build Solution` (or `Ctrl+Shift+B`)
- Start debugging with `F5`.
+### Dedicated server debug arguments
+
+- Default debugger arguments for `Minecraft.Server`:
+ - `-port 25565 -bind 0.0.0.0 -name DedicatedServer`
+- You can override arguments in:
+ - `Project Properties > Debugging > Command Arguments`
+- `Minecraft.Server` post-build copies only the dedicated-server asset set:
+ - `Common/Media/MediaWindows64.arc`
+ - `Common/res`
+ - `Windows64/GameHDD`
+
## CMake (Windows x64)
Configure (use your VS Community instance explicitly):
@@ -32,6 +45,18 @@ Build Release:
cmake --build build --config Release --target MinecraftClient
```
+Build Dedicated Server (Debug):
+
+```powershell
+cmake --build build --config Debug --target MinecraftServer
+```
+
+Build Dedicated Server (Release):
+
+```powershell
+cmake --build build --config Release --target MinecraftServer
+```
+
Run executable:
```powershell
@@ -39,6 +64,13 @@ cd .\build\Debug
.\MinecraftClient.exe
```
+Run dedicated server:
+
+```powershell
+cd .\build\Debug
+.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -name DedicatedServer
+```
+
Notes:
- The CMake build is Windows-only and x64-only.
- Contributors on macOS or Linux need a Windows machine or VM to build the project. Running the game via Wine is separate from having a supported build environment.
diff --git a/Minecraft.Client/Common/Console_Utils.cpp b/Minecraft.Client/Common/Console_Utils.cpp
index cb0f1b58..9a64dbea 100644
--- a/Minecraft.Client/Common/Console_Utils.cpp
+++ b/Minecraft.Client/Common/Console_Utils.cpp
@@ -1,21 +1,32 @@
#include "stdafx.h"
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+#include "..\..\Minecraft.Server\ServerLogManager.h"
+#endif
//--------------------------------------------------------------------------------------
// Name: DebugSpewV()
// Desc: Internal helper function
//--------------------------------------------------------------------------------------
#ifndef _CONTENT_PACKAGE
-static VOID DebugSpewV( const CHAR* strFormat, const va_list pArgList )
+static VOID DebugSpewV( const CHAR* strFormat, va_list pArgList )
{
#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__
- assert(0);
+ assert(0);
#else
- CHAR str[2048];
- // Use the secure CRT to avoid buffer overruns. Specify a count of
- // _TRUNCATE so that too long strings will be silently truncated
- // rather than triggering an error.
- _vsnprintf_s( str, _TRUNCATE, strFormat, pArgList );
- OutputDebugStringA( str );
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ // Dedicated server routes legacy debug spew through ServerLogger to preserve CLI prompt handling.
+ if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
+ {
+ ServerRuntime::ServerLogManager::ForwardClientDebugSpewLogV(strFormat, pArgList);
+ return;
+ }
+#endif
+ CHAR str[2048];
+ // Use the secure CRT to avoid buffer overruns. Specify a count of
+ // _TRUNCATE so that too long strings will be silently truncated
+ // rather than triggering an error.
+ _vsnprintf_s( str, _TRUNCATE, strFormat, pArgList );
+ OutputDebugStringA( str );
#endif
}
#endif
@@ -31,10 +42,9 @@ VOID CDECL DebugPrintf( const CHAR* strFormat, ... )
#endif
{
#ifndef _CONTENT_PACKAGE
- va_list pArgList;
- va_start( pArgList, strFormat );
- DebugSpewV( strFormat, pArgList );
- va_end( pArgList );
+ va_list pArgList;
+ va_start( pArgList, strFormat );
+ DebugSpewV( strFormat, pArgList );
+ va_end( pArgList );
#endif
}
-
diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp
index c3a623d5..0a2fd159 100644
--- a/Minecraft.Client/Common/Consoles_App.cpp
+++ b/Minecraft.Client/Common/Consoles_App.cpp
@@ -38,6 +38,9 @@
#include "GameRules\ConsoleSchematicFile.h"
#include "..\User.h"
#include "..\..\Minecraft.World\LevelData.h"
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+#include "..\..\Minecraft.Server\ServerLogManager.h"
+#endif
#include "..\..\Minecraft.World\net.minecraft.world.entity.player.h"
#include "..\EntityRenderDispatcher.h"
#include "..\..\Minecraft.World\compression.h"
@@ -240,12 +243,21 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...)
{
#ifndef _FINAL_BUILD
- char buf[1024];
- va_list ap;
- va_start(ap, szFormat);
- vsnprintf(buf, sizeof(buf), szFormat, ap);
- va_end(ap);
- OutputDebugStringA(buf);
+ va_list ap;
+ va_start(ap, szFormat);
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ // Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
+ if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
+ {
+ ServerRuntime::ServerLogManager::ForwardClientAppDebugLogV(szFormat, ap);
+ va_end(ap);
+ return;
+ }
+#endif
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), szFormat, ap);
+ va_end(ap);
+ OutputDebugStringA(buf);
#endif
}
@@ -253,53 +265,62 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...)
void CMinecraftApp::DebugPrintf(int user, const char *szFormat, ...)
{
#ifndef _FINAL_BUILD
- if(user == USER_NONE)
- return;
- char buf[1024];
- va_list ap;
- va_start(ap, szFormat);
- vsnprintf(buf, sizeof(buf), szFormat, ap);
- va_end(ap);
+ if(user == USER_NONE)
+ return;
+ va_list ap;
+ va_start(ap, szFormat);
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ // Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe.
+ if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs())
+ {
+ ServerRuntime::ServerLogManager::ForwardClientUserDebugLogV(user, szFormat, ap);
+ va_end(ap);
+ return;
+ }
+#endif
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), szFormat, ap);
+ va_end(ap);
#ifdef __PS3__
- unsigned int writelen;
- sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen );
+ unsigned int writelen;
+ sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen );
#elif defined __PSVITA__
- switch(user)
- {
- case 0:
- {
- SceUID tty2 = sceIoOpen("tty2:", SCE_O_WRONLY, 0);
- if(tty2>=0)
- {
- std::string string1(buf);
- sceIoWrite(tty2, string1.c_str(), string1.length());
- sceIoClose(tty2);
- }
- }
- break;
- case 1:
- {
- SceUID tty3 = sceIoOpen("tty3:", SCE_O_WRONLY, 0);
- if(tty3>=0)
- {
- std::string string1(buf);
- sceIoWrite(tty3, string1.c_str(), string1.length());
- sceIoClose(tty3);
- }
- }
- break;
- default:
- OutputDebugStringA(buf);
- break;
- }
+ switch(user)
+ {
+ case 0:
+ {
+ SceUID tty2 = sceIoOpen("tty2:", SCE_O_WRONLY, 0);
+ if(tty2>=0)
+ {
+ std::string string1(buf);
+ sceIoWrite(tty2, string1.c_str(), string1.length());
+ sceIoClose(tty2);
+ }
+ }
+ break;
+ case 1:
+ {
+ SceUID tty3 = sceIoOpen("tty3:", SCE_O_WRONLY, 0);
+ if(tty3>=0)
+ {
+ std::string string1(buf);
+ sceIoWrite(tty3, string1.c_str(), string1.length());
+ sceIoClose(tty3);
+ }
+ }
+ break;
+ default:
+ OutputDebugStringA(buf);
+ break;
+ }
#else
- OutputDebugStringA(buf);
+ OutputDebugStringA(buf);
#endif
#ifndef _XBOX
- if(user == USER_UI)
- {
- ui.logDebugString(buf);
- }
+ if(user == USER_UI)
+ {
+ ui.logDebugString(buf);
+ }
#endif
#endif
}
diff --git a/Minecraft.Client/Common/Network/GameNetworkManager.cpp b/Minecraft.Client/Common/Network/GameNetworkManager.cpp
index a502dbfb..50aeae68 100644
--- a/Minecraft.Client/Common/Network/GameNetworkManager.cpp
+++ b/Minecraft.Client/Common/Network/GameNetworkManager.cpp
@@ -200,10 +200,12 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
#endif
int64_t seed = 0;
+ bool dedicatedNoLocalHostPlayer = false;
if (lpParameter != nullptr)
{
NetworkGameInitData *param = static_cast<NetworkGameInitData *>(lpParameter);
seed = param->seed;
+ dedicatedNoLocalHostPlayer = param->dedicatedNoLocalHostPlayer;
app.setLevelGenerationOptions(param->levelGen);
if(param->levelGen != nullptr)
@@ -359,9 +361,19 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
// PRIMARY PLAYER
vector<ClientConnection *> createdConnections;
- ClientConnection *connection;
+ ClientConnection *connection = nullptr;
- if( g_NetworkManager.IsHost() )
+ if( g_NetworkManager.IsHost() && dedicatedNoLocalHostPlayer )
+ {
+ app.DebugPrintf("Dedicated server mode: skipping local host client connection\n");
+
+ // Keep telemetry behavior consistent with the host path.
+ INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId();
+ TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId);
+
+ app.SetGameMode( eMode_Multiplayer );
+ }
+ else if( g_NetworkManager.IsHost() )
{
connection = new ClientConnection(minecraft, nullptr);
}
@@ -390,16 +402,18 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
connection = new ClientConnection(minecraft, socket);
}
- if( !connection->createdOk )
+ if (connection != nullptr)
{
- assert(false);
- delete connection;
- connection = nullptr;
- MinecraftServer::HaltServer();
- return false;
- }
+ if( !connection->createdOk )
+ {
+ assert(false);
+ delete connection;
+ connection = nullptr;
+ MinecraftServer::HaltServer();
+ return false;
+ }
- connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
+ connection->send(std::make_shared<PreLoginPacket>(minecraft->user->name));
// Tick connection until we're ready to go. The stages involved in this are:
// (1) Creating the ClientConnection sends a prelogin packet to the server
@@ -434,9 +448,9 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
connection->close();
}
- if( connection->isStarted() && !connection->isClosed() )
- {
- createdConnections.push_back( connection );
+ if( connection->isStarted() && !connection->isClosed() )
+ {
+ createdConnections.push_back( connection );
int primaryPad = ProfileManager.GetPrimaryPad();
app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK);
@@ -533,13 +547,14 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame
}
}
- app.SetGameMode( eMode_Multiplayer );
- }
- else if ( connection->isClosed() || !IsInSession())
- {
+ app.SetGameMode( eMode_Multiplayer );
+ }
+ else if ( connection->isClosed() || !IsInSession())
+ {
// assert(false);
- MinecraftServer::HaltServer();
- return false;
+ MinecraftServer::HaltServer();
+ return false;
+ }
}
diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp
index 7340a7e0..1e625098 100644
--- a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp
+++ b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp
@@ -240,7 +240,13 @@ void CPlatformNetworkManagerStub::DoWork()
qnetPlayer->m_resolvedXuid = INVALID_XUID;
qnetPlayer->m_gamertag[0] = 0;
qnetPlayer->SetCustomDataValue(0);
- while (IQNet::s_playerCount > 1 && IQNet::m_player[IQNet::s_playerCount - 1].GetCustomDataValue() == 0)
+ // Recalculate s_playerCount as the highest active slot + 1.
+ // A blind decrement would hide players at higher-indexed slots when a
+ // lower-indexed player disconnects first: GetPlayerBySmallId scans
+ // [0, s_playerCount) so any slot at or above the decremented count
+ // becomes invisible, causing its disconnect to be missed (ghost player).
+ while (IQNet::s_playerCount > 1 &&
+ IQNet::m_player[IQNet::s_playerCount - 1].GetCustomDataValue() == 0)
IQNet::s_playerCount--;
}
// NOTE: Do NOT call PushFreeSmallId here. The old PlayerConnection's
@@ -257,6 +263,25 @@ void CPlatformNetworkManagerStub::DoWork()
SystemFlagRemoveBySmallId(disconnectedSmallId);
}
}
+
+ // Client-side host disconnect detection:
+ // if TCP is gone, propagate through normal network-disconnect flow so UI returns to menus.
+ // The processing from the Xbox version will be reused.
+ if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost() && !m_bLeavingGame)
+ {
+ if (!WinsockNetLayer::IsConnected())
+ {
+ if (!m_bLeaveGameOnTick)
+ {
+ m_bLeaveGameOnTick = true;
+ g_NetworkManager.HandleDisconnect(false);
+ }
+ }
+ else
+ {
+ m_bLeaveGameOnTick = false;
+ }
+ }
#endif
}
@@ -356,6 +381,7 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
if( m_bLeavingGame ) return true;
m_bLeavingGame = true;
+ m_bLeaveGameOnTick = false;
#ifdef _WINDOWS64
WinsockNetLayer::StopAdvertising();
@@ -404,6 +430,7 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() );
m_bLeavingGame = false;
+ m_bLeaveGameOnTick = false;
m_pIQNet->HostGame();
@@ -433,9 +460,23 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
if (WinsockNetLayer::IsActive())
{
- const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
- unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
- WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
+ // For Dedicated Server, refer to `lan-advertise` in `server.properties`
+ bool enableLanAdvertising = true;
+ if (g_Win64DedicatedServer)
+ {
+ enableLanAdvertising = g_Win64DedicatedServerLanAdvertise;
+ }
+
+ if (enableLanAdvertising)
+ {
+ const wchar_t* hostName = IQNet::m_player[0].m_gamertag;
+ unsigned int settings = app.GetGameHostOption(eGameHostOption_All);
+ WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION);
+ }
+ else
+ {
+ WinsockNetLayer::StopAdvertising();
+ }
}
#endif
//#endif
@@ -463,6 +504,7 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
m_bLeavingGame = false;
+ m_bLeaveGameOnTick = false;
IQNet::s_isHosting = false;
m_pIQNet->ClientJoinGame();
diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp
index 2cf6930a..a545eff8 100644
--- a/Minecraft.Client/MinecraftServer.cpp
+++ b/Minecraft.Client/MinecraftServer.cpp
@@ -569,6 +569,7 @@ MinecraftServer::MinecraftServer()
playerIdleTimeout = 0;
m_postUpdateThread = nullptr;
forceGameType = false;
+ m_spawnProtectionRadius = 0;
commandDispatcher = new ServerCommandDispatcher();
InitializeCriticalSection(&m_consoleInputCS);
@@ -615,6 +616,10 @@ bool MinecraftServer::initServer(int64_t seed, NetworkGameInitData *initData, DW
logger.info("Loading properties");
#endif
settings = new Settings(new File(L"server.properties"));
+ // Dedicated-only: spawn-protection radius in blocks; 0 disables protection.
+ m_spawnProtectionRadius = GetDedicatedServerInt(settings, L"spawn-protection", 0);
+ if (m_spawnProtectionRadius < 0) m_spawnProtectionRadius = 0;
+ if (m_spawnProtectionRadius > 256) m_spawnProtectionRadius = 256;
app.SetGameHostOption(eGameHostOption_Difficulty, GetDedicatedServerInt(settings, L"difficulty", app.GetGameHostOption(eGameHostOption_Difficulty)));
app.SetGameHostOption(eGameHostOption_GameType, GetDedicatedServerInt(settings, L"gamemode", app.GetGameHostOption(eGameHostOption_GameType)));
@@ -631,6 +636,7 @@ bool MinecraftServer::initServer(int64_t seed, NetworkGameInitData *initData, DW
app.DebugPrintf("ServerSettings: pvp is %s\n",(app.GetGameHostOption(eGameHostOption_PvP)>0)?"on":"off");
app.DebugPrintf("ServerSettings: fire spreads is %s\n",(app.GetGameHostOption(eGameHostOption_FireSpreads)>0)?"on":"off");
app.DebugPrintf("ServerSettings: tnt explodes is %s\n",(app.GetGameHostOption(eGameHostOption_TNT)>0)?"on":"off");
+ app.DebugPrintf("ServerSettings: spawn protection radius is %d\n", m_spawnProtectionRadius);
app.DebugPrintf("\n");
// TODO 4J Stu - Init a load of settings based on data passed as params
@@ -1661,7 +1667,9 @@ Level *MinecraftServer::getCommandSenderWorld()
int MinecraftServer::getSpawnProtectionRadius()
{
- return 16;
+ // Client-host mode must never apply dedicated-server spawn protection settings.
+ if (!ShouldUseDedicatedServerProperties()) return 0;
+ return m_spawnProtectionRadius;
}
bool MinecraftServer::isUnderSpawnProtection(Level *level, int x, int y, int z, shared_ptr<Player> player)
diff --git a/Minecraft.Client/MinecraftServer.h b/Minecraft.Client/MinecraftServer.h
index a33888bc..1ed5db9d 100644
--- a/Minecraft.Client/MinecraftServer.h
+++ b/Minecraft.Client/MinecraftServer.h
@@ -44,6 +44,7 @@ typedef struct _NetworkGameInitData
LevelGenerationOptions *levelGen;
DWORD texturePackId;
bool findSeed;
+ bool dedicatedNoLocalHostPlayer;
unsigned int xzSize;
unsigned char hellScale;
ESavePlatform savePlatform;
@@ -57,6 +58,7 @@ typedef struct _NetworkGameInitData
levelGen = nullptr;
texturePackId = 0;
findSeed = false;
+ dedicatedNoLocalHostPlayer = false;
xzSize = LEVEL_LEGACY_WIDTH;
hellScale = HELL_LEVEL_LEGACY_SCALE;
savePlatform = SAVE_FILE_PLATFORM_LOCAL;
@@ -119,6 +121,7 @@ public:
int maxBuildHeight;
int playerIdleTimeout;
bool forceGameType;
+ int m_spawnProtectionRadius;
private:
// 4J Added
diff --git a/Minecraft.Client/PendingConnection.cpp b/Minecraft.Client/PendingConnection.cpp
index 6d5497f0..f24086c1 100644
--- a/Minecraft.Client/PendingConnection.cpp
+++ b/Minecraft.Client/PendingConnection.cpp
@@ -14,6 +14,11 @@
#include "..\Minecraft.World\net.minecraft.world.item.h"
#include "..\Minecraft.World\SharedConstants.h"
#include "Settings.h"
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+#include "..\Minecraft.Server\ServerLogManager.h"
+#include "..\Minecraft.Server\Access\Access.h"
+#include "..\Minecraft.World\Socket.h"
+#endif
// #ifdef __PS3__
// #include "PS3\Network\NetworkPlayerSony.h"
// #endif
@@ -24,6 +29,24 @@ Random *PendingConnection::random = new Random();
bool g_bRejectDuplicateNames = true;
#endif
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+namespace
+{
+ static unsigned char GetPendingConnectionSmallId(Connection *connection)
+ {
+ if (connection != nullptr)
+ {
+ Socket *socket = connection->getSocket();
+ if (socket != nullptr)
+ {
+ return socket->getSmallId();
+ }
+ }
+ return 0;
+ }
+}
+#endif
+
PendingConnection::PendingConnection(MinecraftServer *server, Socket *socket, const wstring& id)
{
// 4J - added initialisers
@@ -180,16 +203,55 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
duplicateXuid = true;
}
+ bool bannedXuid = false;
+ if (loginXuid != INVALID_XUID)
+ {
+ bannedXuid = server->getPlayers()->isXuidBanned(loginXuid);
+ }
+ if (!bannedXuid && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != loginXuid)
+ {
+ bannedXuid = server->getPlayers()->isXuidBanned(packet->m_onlineXuid);
+ }
+
+ bool whitelistSatisfied = true;
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ if (ServerRuntime::Access::IsWhitelistEnabled())
+ {
+ whitelistSatisfied = false;
+ if (loginXuid != INVALID_XUID)
+ {
+ whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(loginXuid);
+ }
+ if (!whitelistSatisfied && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != loginXuid)
+ {
+ whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(packet->m_onlineXuid);
+ }
+ }
+#endif
+
if( sentDisconnect )
{
// Do nothing
}
- else if( server->getPlayers()->isXuidBanned( packet->m_onlineXuid ) )
+ else if (bannedXuid)
+ {
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_BannedXuid);
+#endif
+ disconnect(DisconnectPacket::eDisconnect_Banned);
+ }
+ else if (!whitelistSatisfied)
{
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_NotWhitelisted);
+#endif
disconnect(DisconnectPacket::eDisconnect_Banned);
}
else if (duplicateXuid)
{
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_DuplicateXuid);
+#endif
// Reject the incoming connection — a player with this UID is already
// on the server. Allowing duplicates causes invisible players and
// other undefined behaviour.
@@ -211,6 +273,9 @@ void PendingConnection::handleLogin(shared_ptr<LoginPacket> packet)
}
if (nameTaken)
{
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_DuplicateName);
+#endif
app.DebugPrintf("Rejecting duplicate name: %ls\n", name.c_str());
disconnect(DisconnectPacket::eDisconnect_Banned);
}
@@ -268,6 +333,9 @@ void PendingConnection::handleAcceptedLogin(shared_ptr<LoginPacket> packet)
shared_ptr<ServerPlayer> playerEntity = server->getPlayers()->getPlayerForLogin(this, name, playerXuid,packet->m_onlineXuid);
if (playerEntity != nullptr)
{
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ ServerRuntime::ServerLogManager::OnAcceptedPlayerLogin(GetPendingConnectionSmallId(connection), name);
+#endif
server->getPlayers()->placeNewPlayer(connection, playerEntity, packet);
connection = nullptr; // We've moved responsibility for this over to the new PlayerConnection, nullptr so we don't delete our reference to it here in our dtor
}
@@ -325,4 +393,4 @@ bool PendingConnection::isServerPacketListener()
bool PendingConnection::isDisconnected()
{
return done;
-} \ No newline at end of file
+}
diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp
index d9915cf6..6319b660 100644
--- a/Minecraft.Client/PlayerConnection.cpp
+++ b/Minecraft.Client/PlayerConnection.cpp
@@ -34,9 +34,26 @@
// 4J Added
#include "..\Minecraft.World\net.minecraft.world.item.crafting.h"
#include "Options.h"
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+#include "..\Minecraft.Server\ServerLogManager.h"
+#endif
+
+namespace
+{
+ // Anti-cheat thresholds. Keep server-side checks authoritative even in host mode.
+ // Base max squared displacement allowed per move packet before speed flags trigger.
+ const double kMoveBaseAllowanceSq = 100.0;
+ // Extra squared displacement allowance derived from current server-side velocity.
+ const double kMoveVelocityAllowanceScale = 100.0;
+ // Max squared distance for interact/attack when the target is visible (normal reach).
+ const double kInteractReachSq = 6.0 * 6.0;
+ // Stricter max squared distance used when LOS is blocked to reduce wall-hit abuse.
+ const double kInteractBlockedReachSq = 3.0 * 3.0;
+}
Random PlayerConnection::random;
+
PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player)
{
// 4J - added initialisers
@@ -66,6 +83,13 @@ PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connecti
m_offlineXUID = INVALID_XUID;
m_onlineXUID = INVALID_XUID;
m_bHasClientTickedOnce = false;
+ m_logSmallId = 0;
+
+ // Cache the first valid transport smallId because disconnect teardown can clear it before the server logger runs.
+ if (this->connection != NULL && this->connection->getSocket() != NULL)
+ {
+ m_logSmallId = this->connection->getSocket()->getSmallId();
+ }
setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false);
}
@@ -76,6 +100,17 @@ PlayerConnection::~PlayerConnection()
DeleteCriticalSection(&done_cs);
}
+unsigned char PlayerConnection::getLogSmallId()
+{
+ // Fall back to the live socket only while the cached value is still empty.
+ if (m_logSmallId == 0 && connection != NULL && connection->getSocket() != NULL)
+ {
+ m_logSmallId = connection->getSocket()->getSmallId();
+ }
+
+ return m_logSmallId;
+}
+
void PlayerConnection::tick()
{
if( done ) return;
@@ -118,6 +153,13 @@ void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason)
return;
}
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ ServerRuntime::ServerLogManager::OnPlayerDisconnected(
+ getLogSmallId(),
+ (player != NULL) ? player->name : std::wstring(),
+ reason,
+ true);
+#endif
app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason );
player->disconnect();
@@ -271,16 +313,19 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
double dist = xDist * xDist + yDist * yDist + zDist * zDist;
- // 4J-PB - removing this one for now
- /*if (dist > 100.0f)
+ // Anti-cheat: reject movement packets that exceed server-authoritative bounds.
+ double velocitySq = player->xd * player->xd + player->yd * player->yd + player->zd * player->zd;
+ double maxAllowedSq = kMoveBaseAllowanceSq + (velocitySq * kMoveVelocityAllowanceScale);
+ if (player->isAllowedToFly() || player->gameMode->isCreative())
{
- // logger.warning(player->name + " moved too quickly!");
- disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly);
- // System.out.println("Moved too quickly at " + xt + ", " + yt + ", " + zt);
- // teleport(player->x, player->y, player->z, player->yRot, player->xRot);
- return;
+ // Creative / flight-allowed players can move farther legitimately per tick.
+ maxAllowedSq *= 1.5;
+ }
+ if (dist > maxAllowedSq)
+ {
+ disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly);
+ return;
}
- */
float r = 1 / 16.0f;
bool oldOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty();
@@ -308,8 +353,8 @@ void PlayerConnection::handleMovePlayer(shared_ptr<MovePlayerPacket> packet)
xDist = xt - player->x;
yDist = yt - player->y;
- // 4J-PB - line below will always be true!
- if (yDist > -0.5 || yDist < 0.5)
+ // Clamp tiny Y drift noise to reduce false positives.
+ if (yDist > -0.5 && yDist < 0.5)
{
yDist = 0;
}
@@ -430,7 +475,8 @@ void PlayerConnection::handlePlayerAction(shared_ptr<PlayerActionPacket> packet)
if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK)
{
- if (true) player->gameMode->startDestroyBlock(x, y, z, packet->face); // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from Java 1.6.4) but putting back to old behaviour
+ // Anti-cheat: validate spawn protection on the server for mining start.
+ if (!server->isUnderSpawnProtection(level, x, y, z, player)) player->gameMode->startDestroyBlock(x, y, z, packet->face);
else player->connection->send(std::make_shared<TileUpdatePacket>(x, y, z, level));
}
@@ -458,8 +504,6 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
int face = packet->getFace();
player->resetLastActionTime();
- // 4J Stu - We don't have ops, so just use the levels setting
- bool canEditSpawn = level->canEditSpawn; // = level->dimension->id != 0 || server->players->isOp(player->name);
if (packet->getFace() == 255)
{
if (item == nullptr) return;
@@ -469,7 +513,8 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
{
if (synched && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) < 8 * 8)
{
- if (true) // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from java 1.6.4) but putting back to old behaviour
+ // Anti-cheat: block placement/use must pass server-side spawn protection.
+ if (!server->isUnderSpawnProtection(level, x, y, z, player))
{
player->gameMode->useItemOn(player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ());
}
@@ -538,7 +583,18 @@ void PlayerConnection::handleUseItem(shared_ptr<UseItemPacket> packet)
void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects)
{
EnterCriticalSection(&done_cs);
- if( done ) return;
+ if( done )
+ {
+ LeaveCriticalSection(&done_cs);
+ return;
+ }
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ ServerRuntime::ServerLogManager::OnPlayerDisconnected(
+ getLogSmallId(),
+ (player != NULL) ? player->name : std::wstring(),
+ reason,
+ false);
+#endif
// logger.info(player.name + " lost connection: " + reason);
// 4J-PB - removed, since it needs to be localised in the language the client is in
//server->players->broadcastAll( shared_ptr<ChatPacket>( new ChatPacket(L"�e" + player->name + L" left the game.") ) );
@@ -742,17 +798,16 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
// 4J Stu - If the client says that we hit something, then agree with it. The canSee can fail here as it checks
// a ray from head->head, but we may actually be looking at a different part of the entity that can be seen
// even though the ray is blocked.
- if (target != nullptr) // && player->canSee(target) && player->distanceToSqr(target) < 6 * 6)
+ if (target != nullptr)
{
- //boole canSee = player->canSee(target);
- //double maxDist = 6 * 6;
- //if (!canSee)
- //{
- // maxDist = 3 * 3;
- //}
+ // Anti-cheat: enforce reach and LOS on the server to reject forged hits.
+ bool canSeeTarget = player->canSee(target);
+ double maxDistSq = canSeeTarget ? kInteractReachSq : kInteractBlockedReachSq;
+ if (player->distanceToSqr(target) > maxDistSq)
+ {
+ return;
+ }
- //if (player->distanceToSqr(target) < maxDist)
- //{
if (packet->action == InteractPacket::INTERACT)
{
player->interact(target);
@@ -767,7 +822,6 @@ void PlayerConnection::handleInteract(shared_ptr<InteractPacket> packet)
}
player->attack(target);
}
- //}
}
}
diff --git a/Minecraft.Client/PlayerConnection.h b/Minecraft.Client/PlayerConnection.h
index ff6093a3..0284bc6a 100644
--- a/Minecraft.Client/PlayerConnection.h
+++ b/Minecraft.Client/PlayerConnection.h
@@ -37,6 +37,7 @@ private:
int dropSpamTickCount;
bool m_bHasClientTickedOnce;
+ unsigned char m_logSmallId;
public:
PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr<ServerPlayer> player);
@@ -45,6 +46,10 @@ public:
void disconnect(DisconnectPacket::eDisconnectReason reason);
private:
+ /**
+ * Returns the stable network smallId used by dedicated-server logging and refreshes it from the live socket when possible
+ */
+ unsigned char getLogSmallId();
double xLastOk, yLastOk, zLastOk;
bool synched;
diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp
index 645e3fb5..ba82ec6a 100644
--- a/Minecraft.Client/PlayerList.cpp
+++ b/Minecraft.Client/PlayerList.cpp
@@ -37,6 +37,11 @@
#include "Common\Network\Sony\NetworkPlayerSony.h"
#endif
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+#include "..\Minecraft.Server\Access\Access.h"
+extern bool g_Win64DedicatedServer;
+#endif
+
// 4J - this class is fairly substantially altered as there didn't seem any point in porting code for banning, whitelisting, ops etc.
PlayerList::PlayerList(MinecraftServer *server)
@@ -1674,6 +1679,13 @@ bool PlayerList::isXuidBanned(PlayerUID xuid)
}
}
+#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
+ if (!banned && g_Win64DedicatedServer)
+ {
+ banned = ServerRuntime::Access::IsPlayerBanned(xuid);
+ }
+#endif
+
return banned;
}
diff --git a/Minecraft.Client/ServerPlayerGameMode.cpp b/Minecraft.Client/ServerPlayerGameMode.cpp
index d2dcaaf6..041487f5 100644
--- a/Minecraft.Client/ServerPlayerGameMode.cpp
+++ b/Minecraft.Client/ServerPlayerGameMode.cpp
@@ -176,31 +176,29 @@ void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z)
{
if (x == xDestroyBlock && y == yDestroyBlock && z == zDestroyBlock)
{
- // int ticksSpentDestroying = gameTicks - destroyProgressStart;
-
int t = level->getTile(x, y, z);
if (t != 0)
{
Tile *tile = Tile::tiles[t];
-
- // MGH - removed checking for the destroy progress here, it has already been checked on the client before it sent the packet.
- // fixes issues with this failing to destroy because of packets bunching up
- // float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1);
- // if (destroyProgress >= .7f || bIgnoreDestroyProgress)
+ // Anti-cheat: re-check destroy progress on the server for STOP_DESTROY.
+ int ticksSpentDestroying = gameTicks - destroyProgressStart;
+ float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1);
+ if (destroyProgress >= 1.0f)
{
isDestroyingBlock = false;
level->destroyTileProgress(player->entityId, x, y, z, -1);
destroyBlock(x, y, z);
}
- // else if (!hasDelayedDestroy)
- // {
- // isDestroyingBlock = false;
- // hasDelayedDestroy = true;
- // delayedDestroyX = x;
- // delayedDestroyY = y;
- // delayedDestroyZ = z;
- // delayedTickStart = destroyProgressStart;
- // }
+ else if (!hasDelayedDestroy)
+ {
+ // Keep server-authoritative mining while allowing legit latency to finish via delayed tick progression.
+ isDestroyingBlock = false;
+ hasDelayedDestroy = true;
+ delayedDestroyX = x;
+ delayedDestroyY = y;
+ delayedDestroyZ = z;
+ delayedTickStart = destroyProgressStart;
+ }
}
}
}
@@ -393,4 +391,4 @@ void ServerPlayerGameMode::setGameRules(GameRulesInstance *rules)
{
if(m_gameRules != nullptr) delete m_gameRules;
m_gameRules = rules;
-} \ No newline at end of file
+}
diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp
index 9d73eda8..981ab3ab 100644
--- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp
+++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp
@@ -8,11 +8,20 @@
#include "WinsockNetLayer.h"
#include "..\..\Common\Network\PlatformNetworkManagerStub.h"
#include "..\..\..\Minecraft.World\Socket.h"
+#if defined(MINECRAFT_SERVER_BUILD)
+#include "..\..\..\Minecraft.Server\Access\Access.h"
+#include "..\..\..\Minecraft.Server\ServerLogManager.h"
+#endif
#include "..\..\..\Minecraft.World\DisconnectPacket.h"
#include "..\..\Minecraft.h"
#include "..\4JLibs\inc\4J_Profile.h"
+#include <string>
+
static bool RecvExact(SOCKET sock, BYTE* buf, int len);
+#if defined(MINECRAFT_SERVER_BUILD)
+static bool TryGetNumericRemoteIp(const sockaddr_in &remoteAddress, std::string *outIp);
+#endif
SOCKET WinsockNetLayer::s_listenSocket = INVALID_SOCKET;
SOCKET WinsockNetLayer::s_hostConnectionSocket = INVALID_SOCKET;
@@ -65,6 +74,7 @@ char g_Win64MultiplayerIP[256] = "127.0.0.1";
bool g_Win64DedicatedServer = false;
int g_Win64DedicatedServerPort = WIN64_NET_DEFAULT_PORT;
char g_Win64DedicatedServerBindIP[256] = "";
+bool g_Win64DedicatedServerLanAdvertise = true;
bool WinsockNetLayer::Initialize()
{
@@ -90,7 +100,11 @@ bool WinsockNetLayer::Initialize()
s_initialized = true;
- StartDiscovery();
+ // Dedicated Server does not use LAN session discovery and therefore does not initiate discovery.
+ if (!g_Win64DedicatedServer)
+ {
+ StartDiscovery();
+ }
return true;
}
@@ -512,6 +526,27 @@ static bool RecvExact(SOCKET sock, BYTE* buf, int len)
return true;
}
+#if defined(MINECRAFT_SERVER_BUILD)
+static bool TryGetNumericRemoteIp(const sockaddr_in &remoteAddress, std::string *outIp)
+{
+ if (outIp == nullptr)
+ {
+ return false;
+ }
+
+ outIp->clear();
+ char ipBuffer[64] = {};
+ const char *ip = inet_ntop(AF_INET, (void *)&remoteAddress.sin_addr, ipBuffer, sizeof(ipBuffer));
+ if (ip == nullptr || ip[0] == 0)
+ {
+ return false;
+ }
+
+ *outIp = ip;
+ return true;
+}
+#endif
+
void WinsockNetLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char* data, unsigned int dataSize)
{
INetworkPlayer* pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId);
@@ -546,7 +581,10 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
{
while (s_active)
{
- SOCKET clientSocket = accept(s_listenSocket, nullptr, nullptr);
+ sockaddr_in remoteAddress;
+ ZeroMemory(&remoteAddress, sizeof(remoteAddress));
+ int remoteAddressLength = sizeof(remoteAddress);
+ SOCKET clientSocket = accept(s_listenSocket, (sockaddr*)&remoteAddress, &remoteAddressLength);
if (clientSocket == INVALID_SOCKET)
{
if (s_active)
@@ -557,10 +595,36 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
int noDelay = 1;
setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay));
+#if defined(MINECRAFT_SERVER_BUILD)
+ std::string remoteIp;
+ const bool hasRemoteIp = TryGetNumericRemoteIp(remoteAddress, &remoteIp);
+ const char *remoteIpForLog = hasRemoteIp ? remoteIp.c_str() : "unknown";
+ if (g_Win64DedicatedServer)
+ {
+ ServerRuntime::ServerLogManager::OnIncomingTcpConnection(remoteIpForLog);
+ if (hasRemoteIp && ServerRuntime::Access::IsIpBanned(remoteIp))
+ {
+ ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_BannedIp);
+ SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_Banned);
+ closesocket(clientSocket);
+ continue;
+ }
+ }
+#endif
+
extern QNET_STATE _iQNetStubState;
if (_iQNetStubState != QNET_STATE_GAME_PLAY)
{
- app.DebugPrintf("Win64 LAN: Rejecting connection, game not ready\n");
+#if defined(MINECRAFT_SERVER_BUILD)
+ if (g_Win64DedicatedServer)
+ {
+ ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_GameNotReady);
+ }
+ else
+#endif
+ {
+ app.DebugPrintf("Win64 LAN: Rejecting connection, game not ready\n");
+ }
closesocket(clientSocket);
continue;
}
@@ -568,7 +632,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager;
if (g_pPlatformNetworkManager != nullptr && !g_pPlatformNetworkManager->CanAcceptMoreConnections())
{
- app.DebugPrintf("Win64 LAN: Rejecting connection, server at max players\n");
+#if defined(MINECRAFT_SERVER_BUILD)
+ if (g_Win64DedicatedServer)
+ {
+ ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_ServerFull);
+ }
+ else
+#endif
+ {
+ app.DebugPrintf("Win64 LAN: Rejecting connection, server at max players\n");
+ }
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
closesocket(clientSocket);
continue;
@@ -588,7 +661,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
else
{
LeaveCriticalSection(&s_freeSmallIdLock);
- app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n");
+#if defined(MINECRAFT_SERVER_BUILD)
+ if (g_Win64DedicatedServer)
+ {
+ ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_ServerFull);
+ }
+ else
+#endif
+ {
+ app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n");
+ }
SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_ServerFull);
closesocket(clientSocket);
continue;
@@ -616,7 +698,16 @@ DWORD WINAPI WinsockNetLayer::AcceptThreadProc(LPVOID param)
int connIdx = static_cast<int>(s_connections.size()) - 1;
LeaveCriticalSection(&s_connectionsLock);
- app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId);
+#if defined(MINECRAFT_SERVER_BUILD)
+ if (g_Win64DedicatedServer)
+ {
+ ServerRuntime::ServerLogManager::OnAcceptedTcpConnection(assignedSmallId, remoteIpForLog);
+ }
+ else
+#endif
+ {
+ app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId);
+ }
EnterCriticalSection(&s_smallIdToSocketLock);
s_smallIdToSocket[assignedSmallId] = clientSocket;
diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h
index c5d68975..afccbd66 100644
--- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h
+++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h
@@ -170,5 +170,6 @@ extern char g_Win64MultiplayerIP[256];
extern bool g_Win64DedicatedServer;
extern int g_Win64DedicatedServerPort;
extern char g_Win64DedicatedServerBindIP[256];
+extern bool g_Win64DedicatedServerLanAdvertise;
#endif
diff --git a/Minecraft.Server/Access/Access.cpp b/Minecraft.Server/Access/Access.cpp
new file mode 100644
index 00000000..5767a955
--- /dev/null
+++ b/Minecraft.Server/Access/Access.cpp
@@ -0,0 +1,460 @@
+#include "stdafx.h"
+
+#include "Access.h"
+
+#include "..\Common\StringUtils.h"
+#include "..\ServerLogger.h"
+
+#include <memory>
+#include <mutex>
+
+namespace ServerRuntime
+{
+ namespace Access
+ {
+ namespace
+ {
+ /**
+ * **Access State**
+ *
+ * These features are used extensively from various parts of the code, so safe read/write handling is implemented
+ * Stores the published BAN manager snapshot plus a writer gate for clone-and-publish updates
+ * 公開中のBanManagerスナップショットと更新直列化用ロックを保持する
+ */
+ struct AccessState
+ {
+ std::mutex stateLock;
+ std::mutex writeLock;
+ std::shared_ptr<BanManager> banManager;
+ std::shared_ptr<WhitelistManager> whitelistManager;
+ bool whitelistEnabled = false;
+ };
+
+ AccessState g_accessState;
+
+ /**
+ * Copies the currently published manager pointer so readers can work without holding the publish mutex
+ * 公開中のBanManager共有ポインタを複製取得する
+ */
+ static std::shared_ptr<BanManager> GetBanManagerSnapshot()
+ {
+ std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
+ return g_accessState.banManager;
+ }
+
+ /**
+ * Replaces the shared manager pointer with a fully prepared snapshot in one short critical section
+ * 準備完了したBanManagerスナップショットを短いロックで公開する
+ */
+ static void PublishBanManagerSnapshot(const std::shared_ptr<BanManager> &banManager)
+ {
+ std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
+ g_accessState.banManager = banManager;
+ }
+
+ static std::shared_ptr<WhitelistManager> GetWhitelistManagerSnapshot()
+ {
+ std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
+ return g_accessState.whitelistManager;
+ }
+
+ static void PublishWhitelistManagerSnapshot(const std::shared_ptr<WhitelistManager> &whitelistManager)
+ {
+ std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
+ g_accessState.whitelistManager = whitelistManager;
+ }
+ }
+
+ std::string FormatXuid(PlayerUID xuid)
+ {
+ if (xuid == INVALID_XUID)
+ {
+ return "";
+ }
+
+ char buffer[32] = {};
+ sprintf_s(buffer, sizeof(buffer), "0x%016llx", (unsigned long long)xuid);
+ return buffer;
+ }
+
+ bool TryParseXuid(const std::string &text, PlayerUID *outXuid)
+ {
+ if (outXuid == nullptr)
+ {
+ return false;
+ }
+
+ unsigned long long parsed = 0;
+ if (!StringUtils::TryParseUnsignedLongLong(text, &parsed) || parsed == 0ULL)
+ {
+ return false;
+ }
+
+ *outXuid = (PlayerUID)parsed;
+ return true;
+ }
+
+ bool Initialize(const std::string &baseDirectory, bool whitelistEnabled)
+ {
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+
+ // Build the replacement manager privately so readers keep using the last published snapshot during disk I/O.
+ std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(baseDirectory);
+ std::shared_ptr<WhitelistManager> whitelistManager = std::make_shared<WhitelistManager>(baseDirectory);
+ if (!banManager->EnsureBanFilesExist())
+ {
+ LogError("access", "failed to ensure dedicated server ban files exist");
+ return false;
+ }
+ if (!whitelistManager->EnsureWhitelistFileExists())
+ {
+ LogError("access", "failed to ensure dedicated server whitelist file exists");
+ return false;
+ }
+
+ if (!banManager->Reload())
+ {
+ LogError("access", "failed to load dedicated server ban files");
+ return false;
+ }
+ if (!whitelistManager->Reload())
+ {
+ LogError("access", "failed to load dedicated server whitelist file");
+ return false;
+ }
+
+ std::vector<BannedPlayerEntry> playerEntries;
+ std::vector<BannedIpEntry> ipEntries;
+ std::vector<WhitelistedPlayerEntry> whitelistEntries;
+ banManager->SnapshotBannedPlayers(&playerEntries);
+ banManager->SnapshotBannedIps(&ipEntries);
+ whitelistManager->SnapshotWhitelistedPlayers(&whitelistEntries);
+ PublishBanManagerSnapshot(banManager);
+ PublishWhitelistManagerSnapshot(whitelistManager);
+ {
+ std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
+ g_accessState.whitelistEnabled = whitelistEnabled;
+ }
+
+ LogInfof(
+ "access",
+ "loaded %u player bans, %u ip bans, and %u whitelist entries (whitelist=%s)",
+ (unsigned)playerEntries.size(),
+ (unsigned)ipEntries.size(),
+ (unsigned)whitelistEntries.size(),
+ whitelistEnabled ? "enabled" : "disabled");
+ return true;
+ }
+
+ void Shutdown()
+ {
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ PublishBanManagerSnapshot(std::shared_ptr<BanManager>{});
+ PublishWhitelistManagerSnapshot(std::shared_ptr<WhitelistManager>{});
+ std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
+ g_accessState.whitelistEnabled = false;
+ }
+
+ bool Reload()
+ {
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
+ std::shared_ptr<WhitelistManager> currentWhitelist = GetWhitelistManagerSnapshot();
+ if (current == nullptr || currentWhitelist == nullptr)
+ {
+ return false;
+ }
+
+ std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
+ std::shared_ptr<WhitelistManager> whitelistManager = std::make_shared<WhitelistManager>(*currentWhitelist);
+ if (!banManager->EnsureBanFilesExist())
+ {
+ return false;
+ }
+ if (!whitelistManager->EnsureWhitelistFileExists())
+ {
+ return false;
+ }
+ if (!banManager->Reload())
+ {
+ return false;
+ }
+ if (!whitelistManager->Reload())
+ {
+ return false;
+ }
+
+ PublishBanManagerSnapshot(banManager);
+ PublishWhitelistManagerSnapshot(whitelistManager);
+ return true;
+ }
+
+ bool ReloadWhitelist()
+ {
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ const auto current = GetWhitelistManagerSnapshot();
+ if (current == nullptr)
+ {
+ return false;
+ }
+
+ auto whitelistManager = std::make_shared<WhitelistManager>(*current);
+ if (!whitelistManager->EnsureWhitelistFileExists())
+ {
+ return false;
+ }
+ if (!whitelistManager->Reload())
+ {
+ return false;
+ }
+
+ PublishWhitelistManagerSnapshot(whitelistManager);
+ return true;
+ }
+
+ bool IsInitialized()
+ {
+ return GetBanManagerSnapshot() != nullptr && GetWhitelistManagerSnapshot() != nullptr;
+ }
+
+ bool IsWhitelistEnabled()
+ {
+ std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
+ return g_accessState.whitelistEnabled;
+ }
+
+ void SetWhitelistEnabled(bool enabled)
+ {
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ std::lock_guard<std::mutex> stateLock(g_accessState.stateLock);
+ g_accessState.whitelistEnabled = enabled;
+ }
+
+ bool IsPlayerBanned(PlayerUID xuid)
+ {
+ const std::string formatted = FormatXuid(xuid);
+ if (formatted.empty())
+ {
+ return false;
+ }
+
+ std::shared_ptr<BanManager> banManager = GetBanManagerSnapshot();
+ return (banManager != nullptr) ? banManager->IsPlayerBannedByXuid(formatted) : false;
+ }
+
+ bool IsIpBanned(const std::string &ip)
+ {
+ std::shared_ptr<BanManager> banManager = GetBanManagerSnapshot();
+ return (banManager != nullptr) ? banManager->IsIpBanned(ip) : false;
+ }
+
+ bool IsPlayerWhitelisted(PlayerUID xuid)
+ {
+ const std::string formatted = FormatXuid(xuid);
+ if (formatted.empty())
+ {
+ return false;
+ }
+
+ std::shared_ptr<WhitelistManager> whitelistManager = GetWhitelistManagerSnapshot();
+ return (whitelistManager != nullptr) ? whitelistManager->IsPlayerWhitelistedByXuid(formatted) : false;
+ }
+
+ bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata)
+ {
+ const std::string formatted = FormatXuid(xuid);
+ if (formatted.empty())
+ {
+ return false;
+ }
+
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
+ if (current == nullptr)
+ {
+ return false;
+ }
+
+ std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
+ BannedPlayerEntry entry;
+ entry.xuid = formatted;
+ entry.name = name;
+ entry.metadata = metadata;
+ if (!banManager->AddPlayerBan(entry))
+ {
+ return false;
+ }
+
+ PublishBanManagerSnapshot(banManager);
+ return true;
+ }
+
+ bool AddIpBan(const std::string &ip, const BanMetadata &metadata)
+ {
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
+ if (current == nullptr)
+ {
+ return false;
+ }
+
+ std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
+ BannedIpEntry entry;
+ entry.ip = ip;
+ entry.metadata = metadata;
+ if (!banManager->AddIpBan(entry))
+ {
+ return false;
+ }
+
+ PublishBanManagerSnapshot(banManager);
+ return true;
+ }
+
+ bool RemovePlayerBan(PlayerUID xuid)
+ {
+ const std::string formatted = FormatXuid(xuid);
+ if (formatted.empty())
+ {
+ return false;
+ }
+
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
+ if (current == nullptr)
+ {
+ return false;
+ }
+
+ std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
+ if (!banManager->RemovePlayerBanByXuid(formatted))
+ {
+ return false;
+ }
+
+ PublishBanManagerSnapshot(banManager);
+ return true;
+ }
+
+ bool RemoveIpBan(const std::string &ip)
+ {
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ std::shared_ptr<BanManager> current = GetBanManagerSnapshot();
+ if (current == nullptr)
+ {
+ return false;
+ }
+
+ std::shared_ptr<BanManager> banManager = std::make_shared<BanManager>(*current);
+ if (!banManager->RemoveIpBan(ip))
+ {
+ return false;
+ }
+
+ PublishBanManagerSnapshot(banManager);
+ return true;
+ }
+
+ bool AddWhitelistedPlayer(PlayerUID xuid, const std::string &name, const WhitelistMetadata &metadata)
+ {
+ const auto formatted = FormatXuid(xuid);
+ if (formatted.empty())
+ {
+ return false;
+ }
+
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ const auto current = GetWhitelistManagerSnapshot();
+ if (current == nullptr)
+ {
+ return false;
+ }
+
+ auto whitelistManager = std::make_shared<WhitelistManager>(*current);
+ const WhitelistedPlayerEntry entry = { formatted, name, metadata };
+ if (!whitelistManager->AddPlayer(entry))
+ {
+ return false;
+ }
+
+ PublishWhitelistManagerSnapshot(whitelistManager);
+ return true;
+ }
+
+ bool RemoveWhitelistedPlayer(PlayerUID xuid)
+ {
+ const auto formatted = FormatXuid(xuid);
+ if (formatted.empty())
+ {
+ return false;
+ }
+
+ std::lock_guard<std::mutex> writeLock(g_accessState.writeLock);
+ const auto current = GetWhitelistManagerSnapshot();
+ if (current == nullptr)
+ {
+ return false;
+ }
+
+ auto whitelistManager = std::make_shared<WhitelistManager>(*current);
+ if (!whitelistManager->RemovePlayerByXuid(formatted))
+ {
+ return false;
+ }
+
+ PublishWhitelistManagerSnapshot(whitelistManager);
+ return true;
+ }
+
+ bool SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries)
+ {
+ if (outEntries == nullptr)
+ {
+ return false;
+ }
+
+ std::shared_ptr<BanManager> banManager = GetBanManagerSnapshot();
+ if (banManager == nullptr)
+ {
+ outEntries->clear();
+ return false;
+ }
+
+ return banManager->SnapshotBannedPlayers(outEntries);
+ }
+
+ bool SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries)
+ {
+ if (outEntries == nullptr)
+ {
+ return false;
+ }
+
+ std::shared_ptr<BanManager> banManager = GetBanManagerSnapshot();
+ if (banManager == nullptr)
+ {
+ outEntries->clear();
+ return false;
+ }
+
+ return banManager->SnapshotBannedIps(outEntries);
+ }
+
+ bool SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries)
+ {
+ if (outEntries == nullptr)
+ {
+ return false;
+ }
+
+ const auto whitelistManager = GetWhitelistManagerSnapshot();
+ if (whitelistManager == nullptr)
+ {
+ outEntries->clear();
+ return false;
+ }
+
+ return whitelistManager->SnapshotWhitelistedPlayers(outEntries);
+ }
+ }
+}
diff --git a/Minecraft.Server/Access/Access.h b/Minecraft.Server/Access/Access.h
new file mode 100644
index 00000000..80e61e55
--- /dev/null
+++ b/Minecraft.Server/Access/Access.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include "BanManager.h"
+#include "WhitelistManager.h"
+
+namespace ServerRuntime
+{
+ /**
+ * A frontend that will be general-purpose, assuming the implementation of whitelists and ops in the future.
+ */
+ namespace Access
+ {
+ bool Initialize(const std::string &baseDirectory = ".", bool whitelistEnabled = false);
+ void Shutdown();
+ bool Reload();
+ bool ReloadWhitelist();
+ bool IsInitialized();
+ bool IsWhitelistEnabled();
+ void SetWhitelistEnabled(bool enabled);
+
+ bool IsPlayerBanned(PlayerUID xuid);
+ bool IsIpBanned(const std::string &ip);
+ bool IsPlayerWhitelisted(PlayerUID xuid);
+
+ bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata);
+ bool AddIpBan(const std::string &ip, const BanMetadata &metadata);
+ bool RemovePlayerBan(PlayerUID xuid);
+ bool RemoveIpBan(const std::string &ip);
+ bool AddWhitelistedPlayer(PlayerUID xuid, const std::string &name, const WhitelistMetadata &metadata);
+ bool RemoveWhitelistedPlayer(PlayerUID xuid);
+
+ /**
+ * Copies the current cached player bans for inspection or command output
+ * 現在のプレイヤーBAN一覧を複製取得
+ */
+ bool SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries);
+ /**
+ * Copies the current cached IP bans for inspection or command output
+ * 現在のIP BAN一覧を複製取得
+ */
+ bool SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries);
+ bool SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries);
+
+ std::string FormatXuid(PlayerUID xuid);
+ bool TryParseXuid(const std::string &text, PlayerUID *outXuid);
+ }
+}
diff --git a/Minecraft.Server/Access/BanManager.cpp b/Minecraft.Server/Access/BanManager.cpp
new file mode 100644
index 00000000..59f5bccc
--- /dev/null
+++ b/Minecraft.Server/Access/BanManager.cpp
@@ -0,0 +1,631 @@
+#include "stdafx.h"
+
+#include "BanManager.h"
+
+#include "..\Common\AccessStorageUtils.h"
+#include "..\Common\FileUtils.h"
+#include "..\Common\NetworkUtils.h"
+#include "..\Common\StringUtils.h"
+#include "..\ServerLogger.h"
+#include "..\vendor\nlohmann\json.hpp"
+
+#include <algorithm>
+#include <stdio.h>
+
+namespace ServerRuntime
+{
+ namespace Access
+ {
+ using OrderedJson = nlohmann::ordered_json;
+
+ namespace
+ {
+ static const char *kBannedPlayersFileName = "banned-players.json";
+ static const char *kBannedIpsFileName = "banned-ips.json";
+
+ static bool TryParseUtcTimestamp(const std::string &text, unsigned long long *outFileTime)
+ {
+ if (outFileTime == nullptr)
+ {
+ return false;
+ }
+
+ std::string trimmed = StringUtils::TrimAscii(text);
+ if (trimmed.empty())
+ {
+ return false;
+ }
+
+ unsigned year = 0;
+ unsigned month = 0;
+ unsigned day = 0;
+ unsigned hour = 0;
+ unsigned minute = 0;
+ unsigned second = 0;
+ if (sscanf_s(trimmed.c_str(), "%4u-%2u-%2uT%2u:%2u:%2uZ", &year, &month, &day, &hour, &minute, &second) != 6)
+ {
+ return false;
+ }
+
+ SYSTEMTIME utc = {};
+ utc.wYear = (WORD)year;
+ utc.wMonth = (WORD)month;
+ utc.wDay = (WORD)day;
+ utc.wHour = (WORD)hour;
+ utc.wMinute = (WORD)minute;
+ utc.wSecond = (WORD)second;
+
+ FILETIME fileTime = {};
+ if (!SystemTimeToFileTime(&utc, &fileTime))
+ {
+ return false;
+ }
+
+ ULARGE_INTEGER value = {};
+ value.LowPart = fileTime.dwLowDateTime;
+ value.HighPart = fileTime.dwHighDateTime;
+ *outFileTime = value.QuadPart;
+ return true;
+ }
+
+ static bool IsMetadataExpired(const BanMetadata &metadata, unsigned long long nowFileTime)
+ {
+ if (metadata.expires.empty())
+ {
+ return false;
+ }
+
+ unsigned long long expiresFileTime = 0;
+ if (!TryParseUtcTimestamp(metadata.expires, &expiresFileTime))
+ {
+ // Keep malformed metadata active instead of silently unbanning a player or address.
+ return false;
+ }
+
+ return expiresFileTime <= nowFileTime;
+ }
+ }
+ BanManager::BanManager(const std::string &baseDirectory)
+ : m_baseDirectory(baseDirectory.empty() ? "." : baseDirectory)
+ {
+ }
+
+ bool BanManager::EnsureBanFilesExist() const
+ {
+ const std::string playersPath = GetBannedPlayersFilePath();
+ const std::string ipsPath = GetBannedIpsFilePath();
+
+ const bool playersOk = AccessStorageUtils::EnsureJsonListFileExists(playersPath);
+ const bool ipsOk = AccessStorageUtils::EnsureJsonListFileExists(ipsPath);
+ if (!playersOk)
+ {
+ LogErrorf("access", "failed to create %s", playersPath.c_str());
+ }
+ if (!ipsOk)
+ {
+ LogErrorf("access", "failed to create %s", ipsPath.c_str());
+ }
+ return playersOk && ipsOk;
+ }
+
+ bool BanManager::Reload()
+ {
+ std::vector<BannedPlayerEntry> players;
+ std::vector<BannedIpEntry> ips;
+
+ if (!LoadPlayers(&players))
+ {
+ return false;
+ }
+ if (!LoadIps(&ips))
+ {
+ return false;
+ }
+
+ m_bannedPlayers.swap(players);
+ m_bannedIps.swap(ips);
+ return true;
+ }
+
+ bool BanManager::Save() const
+ {
+ std::vector<BannedPlayerEntry> players;
+ std::vector<BannedIpEntry> ips;
+ return SnapshotBannedPlayers(&players) &&
+ SnapshotBannedIps(&ips) &&
+ SavePlayers(players) &&
+ SaveIps(ips);
+ }
+ bool BanManager::LoadPlayers(std::vector<BannedPlayerEntry> *outEntries) const
+ {
+ if (outEntries == nullptr)
+ {
+ return false;
+ }
+ outEntries->clear();
+
+ std::string text;
+ const std::string path = GetBannedPlayersFilePath();
+ if (!FileUtils::ReadTextFile(path, &text))
+ {
+ LogErrorf("access", "failed to read %s", path.c_str());
+ return false;
+ }
+ if (text.empty())
+ {
+ text = "[]";
+ }
+
+ OrderedJson root;
+ try
+ {
+ // Strip an optional UTF-8 BOM because some editors prepend it when rewriting JSON files.
+ root = OrderedJson::parse(StringUtils::StripUtf8Bom(text));
+ }
+ catch (const nlohmann::json::exception &e)
+ {
+ LogErrorf("access", "failed to parse %s: %s", path.c_str(), e.what());
+ return false;
+ }
+
+ if (!root.is_array())
+ {
+ LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str());
+ return false;
+ }
+
+ const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
+ for (const auto &object : root)
+ {
+ if (!object.is_object())
+ {
+ LogWarnf("access", "skipping banned player entry that is not an object in %s", path.c_str());
+ continue;
+ }
+
+ std::string rawXuid;
+ if (!AccessStorageUtils::TryGetStringField(object, "xuid", &rawXuid))
+ {
+ LogWarnf("access", "skipping banned player entry without xuid in %s", path.c_str());
+ continue;
+ }
+
+ BannedPlayerEntry entry;
+ entry.xuid = AccessStorageUtils::NormalizeXuid(rawXuid);
+ if (entry.xuid.empty())
+ {
+ LogWarnf("access", "skipping banned player entry with empty xuid in %s", path.c_str());
+ continue;
+ }
+
+ AccessStorageUtils::TryGetStringField(object, "name", &entry.name);
+ AccessStorageUtils::TryGetStringField(object, "created", &entry.metadata.created);
+ AccessStorageUtils::TryGetStringField(object, "source", &entry.metadata.source);
+ AccessStorageUtils::TryGetStringField(object, "expires", &entry.metadata.expires);
+ AccessStorageUtils::TryGetStringField(object, "reason", &entry.metadata.reason);
+ NormalizeMetadata(&entry.metadata);
+
+ // Ignore entries that already expired before reload so the in-memory cache starts from the active set.
+ if (IsMetadataExpired(entry.metadata, nowFileTime))
+ {
+ continue;
+ }
+
+ outEntries->push_back(entry);
+ }
+
+ return true;
+ }
+ bool BanManager::LoadIps(std::vector<BannedIpEntry> *outEntries) const
+ {
+ if (outEntries == nullptr)
+ {
+ return false;
+ }
+ outEntries->clear();
+
+ std::string text;
+ const std::string path = GetBannedIpsFilePath();
+ if (!FileUtils::ReadTextFile(path, &text))
+ {
+ LogErrorf("access", "failed to read %s", path.c_str());
+ return false;
+ }
+ if (text.empty())
+ {
+ text = "[]";
+ }
+
+ OrderedJson root;
+ try
+ {
+ // Strip an optional UTF-8 BOM because some editors prepend it when rewriting JSON files.
+ root = OrderedJson::parse(StringUtils::StripUtf8Bom(text));
+ }
+ catch (const nlohmann::json::exception &e)
+ {
+ LogErrorf("access", "failed to parse %s: %s", path.c_str(), e.what());
+ return false;
+ }
+
+ if (!root.is_array())
+ {
+ LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str());
+ return false;
+ }
+
+ const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
+ for (const auto &object : root)
+ {
+ if (!object.is_object())
+ {
+ LogWarnf("access", "skipping banned ip entry that is not an object in %s", path.c_str());
+ continue;
+ }
+
+ std::string rawIp;
+ if (!AccessStorageUtils::TryGetStringField(object, "ip", &rawIp))
+ {
+ LogWarnf("access", "skipping banned ip entry without ip in %s", path.c_str());
+ continue;
+ }
+
+ BannedIpEntry entry;
+ entry.ip = NetworkUtils::NormalizeIpToken(rawIp);
+ if (entry.ip.empty())
+ {
+ LogWarnf("access", "skipping banned ip entry with empty ip in %s", path.c_str());
+ continue;
+ }
+
+ AccessStorageUtils::TryGetStringField(object, "created", &entry.metadata.created);
+ AccessStorageUtils::TryGetStringField(object, "source", &entry.metadata.source);
+ AccessStorageUtils::TryGetStringField(object, "expires", &entry.metadata.expires);
+ AccessStorageUtils::TryGetStringField(object, "reason", &entry.metadata.reason);
+ NormalizeMetadata(&entry.metadata);
+
+ // Ignore entries that already expired before reload so the in-memory cache starts from the active set.
+ if (IsMetadataExpired(entry.metadata, nowFileTime))
+ {
+ continue;
+ }
+
+ outEntries->push_back(entry);
+ }
+
+ return true;
+ }
+ bool BanManager::SavePlayers(const std::vector<BannedPlayerEntry> &entries) const
+ {
+ OrderedJson root = OrderedJson::array();
+ for (const auto &entry : entries)
+ {
+ OrderedJson object = OrderedJson::object();
+ object["xuid"] = AccessStorageUtils::NormalizeXuid(entry.xuid);
+ object["name"] = entry.name;
+ object["created"] = entry.metadata.created;
+ object["source"] = entry.metadata.source;
+ object["expires"] = entry.metadata.expires;
+ object["reason"] = entry.metadata.reason;
+ root.push_back(object);
+ }
+
+ const std::string path = GetBannedPlayersFilePath();
+ const std::string json = root.empty() ? std::string("[]\n") : (root.dump(2) + "\n");
+ if (!FileUtils::WriteTextFileAtomic(path, json))
+ {
+ LogErrorf("access", "failed to write %s", path.c_str());
+ return false;
+ }
+ return true;
+ }
+
+ bool BanManager::SaveIps(const std::vector<BannedIpEntry> &entries) const
+ {
+ OrderedJson root = OrderedJson::array();
+ for (const auto &entry : entries)
+ {
+ OrderedJson object = OrderedJson::object();
+ object["ip"] = NetworkUtils::NormalizeIpToken(entry.ip);
+ object["created"] = entry.metadata.created;
+ object["source"] = entry.metadata.source;
+ object["expires"] = entry.metadata.expires;
+ object["reason"] = entry.metadata.reason;
+ root.push_back(object);
+ }
+
+ const std::string path = GetBannedIpsFilePath();
+ const std::string json = root.empty() ? std::string("[]\n") : (root.dump(2) + "\n");
+ if (!FileUtils::WriteTextFileAtomic(path, json))
+ {
+ LogErrorf("access", "failed to write %s", path.c_str());
+ return false;
+ }
+ return true;
+ }
+
+ const std::vector<BannedPlayerEntry> &BanManager::GetBannedPlayers() const
+ {
+ return m_bannedPlayers;
+ }
+
+ const std::vector<BannedIpEntry> &BanManager::GetBannedIps() const
+ {
+ return m_bannedIps;
+ }
+
+ bool BanManager::SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries) const
+ {
+ if (outEntries == nullptr)
+ {
+ return false;
+ }
+
+ outEntries->clear();
+ outEntries->reserve(m_bannedPlayers.size());
+
+ const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
+ for (const auto &entry : m_bannedPlayers)
+ {
+ if (!IsMetadataExpired(entry.metadata, nowFileTime))
+ {
+ outEntries->push_back(entry);
+ }
+ }
+ return true;
+ }
+
+ bool BanManager::SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries) const
+ {
+ if (outEntries == nullptr)
+ {
+ return false;
+ }
+
+ outEntries->clear();
+ outEntries->reserve(m_bannedIps.size());
+
+ const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
+ for (const auto &entry : m_bannedIps)
+ {
+ if (!IsMetadataExpired(entry.metadata, nowFileTime))
+ {
+ outEntries->push_back(entry);
+ }
+ }
+ return true;
+ }
+ bool BanManager::IsPlayerBannedByXuid(const std::string &xuid) const
+ {
+ const std::string normalized = AccessStorageUtils::NormalizeXuid(xuid);
+ if (normalized.empty())
+ {
+ return false;
+ }
+
+ const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
+ return std::any_of(
+ m_bannedPlayers.begin(),
+ m_bannedPlayers.end(),
+ [&normalized, nowFileTime](const BannedPlayerEntry &entry)
+ {
+ return entry.xuid == normalized && !IsMetadataExpired(entry.metadata, nowFileTime);
+ });
+ }
+
+ bool BanManager::IsIpBanned(const std::string &ip) const
+ {
+ const std::string normalized = NetworkUtils::NormalizeIpToken(ip);
+ if (normalized.empty())
+ {
+ return false;
+ }
+
+ const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime();
+ return std::any_of(
+ m_bannedIps.begin(),
+ m_bannedIps.end(),
+ [&normalized, nowFileTime](const BannedIpEntry &entry)
+ {
+ return entry.ip == normalized && !IsMetadataExpired(entry.metadata, nowFileTime);
+ });
+ }
+
+ bool BanManager::AddPlayerBan(const BannedPlayerEntry &entry)
+ {
+ std::vector<BannedPlayerEntry> updatedEntries;
+ if (!SnapshotBannedPlayers(&updatedEntries))
+ {
+ return false;
+ }
+
+ BannedPlayerEntry normalized = entry;
+ normalized.xuid = AccessStorageUtils::NormalizeXuid(normalized.xuid);
+ NormalizeMetadata(&normalized.metadata);
+ if (normalized.xuid.empty())
+ {
+ return false;
+ }
+
+ const auto existing = std::find_if(
+ updatedEntries.begin(),
+ updatedEntries.end(),
+ [&normalized](const BannedPlayerEntry &candidate)
+ {
+ return candidate.xuid == normalized.xuid;
+ });
+ if (existing != updatedEntries.end())
+ {
+ // Update the existing entry in place so the stored list remains unique by canonical XUID.
+ *existing = normalized;
+ if (!SavePlayers(updatedEntries))
+ {
+ return false;
+ }
+ m_bannedPlayers.swap(updatedEntries);
+ return true;
+ }
+
+ updatedEntries.push_back(normalized);
+ if (!SavePlayers(updatedEntries))
+ {
+ return false;
+ }
+ m_bannedPlayers.swap(updatedEntries);
+ return true;
+ }
+
+ bool BanManager::AddIpBan(const BannedIpEntry &entry)
+ {
+ std::vector<BannedIpEntry> updatedEntries;
+ if (!SnapshotBannedIps(&updatedEntries))
+ {
+ return false;
+ }
+
+ BannedIpEntry normalized = entry;
+ normalized.ip = NetworkUtils::NormalizeIpToken(normalized.ip);
+ NormalizeMetadata(&normalized.metadata);
+ if (normalized.ip.empty())
+ {
+ return false;
+ }
+
+ const auto existing = std::find_if(
+ updatedEntries.begin(),
+ updatedEntries.end(),
+ [&normalized](const BannedIpEntry &candidate)
+ {
+ return candidate.ip == normalized.ip;
+ });
+ if (existing != updatedEntries.end())
+ {
+ // Update the existing entry in place so the stored list remains unique by normalized IP.
+ *existing = normalized;
+ if (!SaveIps(updatedEntries))
+ {
+ return false;
+ }
+ m_bannedIps.swap(updatedEntries);
+ return true;
+ }
+
+ updatedEntries.push_back(normalized);
+ if (!SaveIps(updatedEntries))
+ {
+ return false;
+ }
+ m_bannedIps.swap(updatedEntries);
+ return true;
+ }
+
+ bool BanManager::RemovePlayerBanByXuid(const std::string &xuid)
+ {
+ const std::string normalized = AccessStorageUtils::NormalizeXuid(xuid);
+ if (normalized.empty())
+ {
+ return false;
+ }
+
+ std::vector<BannedPlayerEntry> updatedEntries;
+ if (!SnapshotBannedPlayers(&updatedEntries))
+ {
+ return false;
+ }
+
+ size_t oldSize = updatedEntries.size();
+ updatedEntries.erase(
+ std::remove_if(
+ updatedEntries.begin(),
+ updatedEntries.end(),
+ [&normalized](const BannedPlayerEntry &entry) { return entry.xuid == normalized; }),
+ updatedEntries.end());
+
+ if (updatedEntries.size() == oldSize)
+ {
+ return false;
+ }
+ if (!SavePlayers(updatedEntries))
+ {
+ return false;
+ }
+ m_bannedPlayers.swap(updatedEntries);
+ return true;
+ }
+
+
+ bool BanManager::RemoveIpBan(const std::string &ip)
+ {
+ const std::string normalized = NetworkUtils::NormalizeIpToken(ip);
+ if (normalized.empty())
+ {
+ return false;
+ }
+
+ std::vector<BannedIpEntry> updatedEntries;
+ if (!SnapshotBannedIps(&updatedEntries))
+ {
+ return false;
+ }
+
+ size_t oldSize = updatedEntries.size();
+ updatedEntries.erase(
+ std::remove_if(
+ updatedEntries.begin(),
+ updatedEntries.end(),
+ [&normalized](const BannedIpEntry &entry) { return entry.ip == normalized; }),
+ updatedEntries.end());
+
+ if (updatedEntries.size() == oldSize)
+ {
+ return false;
+ }
+ if (!SaveIps(updatedEntries))
+ {
+ return false;
+ }
+ m_bannedIps.swap(updatedEntries);
+ return true;
+ }
+ std::string BanManager::GetBannedPlayersFilePath() const
+ {
+ return BuildPath(kBannedPlayersFileName);
+ }
+
+ std::string BanManager::GetBannedIpsFilePath() const
+ {
+ return BuildPath(kBannedIpsFileName);
+ }
+
+
+ BanMetadata BanManager::BuildDefaultMetadata(const char *source)
+ {
+ BanMetadata metadata;
+ metadata.created = StringUtils::GetCurrentUtcTimestampIso8601();
+ metadata.source = (source != nullptr) ? source : "Server";
+ metadata.expires = "";
+ metadata.reason = "";
+ return metadata;
+ }
+
+
+ void BanManager::NormalizeMetadata(BanMetadata *metadata)
+ {
+ if (metadata == nullptr)
+ {
+ return;
+ }
+
+ metadata->created = StringUtils::TrimAscii(metadata->created);
+ metadata->source = StringUtils::TrimAscii(metadata->source);
+ metadata->expires = StringUtils::TrimAscii(metadata->expires);
+ metadata->reason = StringUtils::TrimAscii(metadata->reason);
+ }
+
+
+ std::string BanManager::BuildPath(const char *fileName) const
+ {
+ return AccessStorageUtils::BuildPathFromBaseDirectory(m_baseDirectory, fileName);
+ }
+ }
+}
diff --git a/Minecraft.Server/Access/BanManager.h b/Minecraft.Server/Access/BanManager.h
new file mode 100644
index 00000000..59103bec
--- /dev/null
+++ b/Minecraft.Server/Access/BanManager.h
@@ -0,0 +1,106 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace ServerRuntime
+{
+ namespace Access
+ {
+ /**
+ * Information shared with player bans and IP bans
+ * プレイヤーBANとIP BANで共有する情報
+ */
+ struct BanMetadata
+ {
+ std::string created;
+ std::string source;
+ std::string expires;
+ std::string reason;
+ };
+
+ struct BannedPlayerEntry
+ {
+ std::string xuid;
+ std::string name;
+ BanMetadata metadata;
+ };
+
+ struct BannedIpEntry
+ {
+ std::string ip;
+ BanMetadata metadata;
+ };
+
+ /**
+ * Dedicated server BAN file manager.
+ *
+ * Files:
+ * - banned-players.json
+ * - banned-ips.json
+ *
+ * This class only handles storage/caching.
+ * Connection-time hooks are wired separately.
+ */
+ class BanManager
+ {
+ public:
+ /**
+ * **Create Ban Manager**
+ *
+ * Binds the manager to the directory that stores the dedicated-server access files
+ * Dedicated Server のアクセスファイル配置先を設定する
+ */
+ explicit BanManager(const std::string &baseDirectory = ".");
+
+ /**
+ * Creates empty JSON array files when the dedicated server starts without persisted access data
+ * BANファイルが無い初回起動時に空JSONを用意する
+ */
+ bool EnsureBanFilesExist() const;
+ bool Reload();
+ bool Save() const;
+
+ bool LoadPlayers(std::vector<BannedPlayerEntry> *outEntries) const;
+ bool LoadIps(std::vector<BannedIpEntry> *outEntries) const;
+ bool SavePlayers(const std::vector<BannedPlayerEntry> &entries) const;
+ bool SaveIps(const std::vector<BannedIpEntry> &entries) const;
+
+ const std::vector<BannedPlayerEntry> &GetBannedPlayers() const;
+ const std::vector<BannedIpEntry> &GetBannedIps() const;
+ /**
+ * Copies only currently active player BAN entries so expired metadata does not leak into command output
+ * 期限切れを除いた有効なプレイヤーBAN一覧を複製取得する
+ */
+ bool SnapshotBannedPlayers(std::vector<BannedPlayerEntry> *outEntries) const;
+ /**
+ * Copies only currently active IP BAN entries so expired metadata does not leak into command output
+ * 期限切れを除いた有効なIP BAN一覧を複製取得する
+ */
+ bool SnapshotBannedIps(std::vector<BannedIpEntry> *outEntries) const;
+
+ bool IsPlayerBannedByXuid(const std::string &xuid) const;
+ bool IsIpBanned(const std::string &ip) const;
+
+ bool AddPlayerBan(const BannedPlayerEntry &entry);
+ bool AddIpBan(const BannedIpEntry &entry);
+ bool RemovePlayerBanByXuid(const std::string &xuid);
+ bool RemoveIpBan(const std::string &ip);
+
+ std::string GetBannedPlayersFilePath() const;
+ std::string GetBannedIpsFilePath() const;
+
+ static BanMetadata BuildDefaultMetadata(const char *source = "Server");
+
+ private:
+ static void NormalizeMetadata(BanMetadata *metadata);
+
+ std::string BuildPath(const char *fileName) const;
+
+ private:
+ std::string m_baseDirectory;
+ std::vector<BannedPlayerEntry> m_bannedPlayers;
+ std::vector<BannedIpEntry> m_bannedIps;
+ };
+ }
+}
diff --git a/Minecraft.Server/Access/WhitelistManager.cpp b/Minecraft.Server/Access/WhitelistManager.cpp
new file mode 100644
index 00000000..33ea7e46
--- /dev/null
+++ b/Minecraft.Server/Access/WhitelistManager.cpp
@@ -0,0 +1,297 @@
+#include "stdafx.h"
+
+#include "WhitelistManager.h"
+
+#include "..\Common\AccessStorageUtils.h"
+#include "..\Common\FileUtils.h"
+#include "..\Common\StringUtils.h"
+#include "..\ServerLogger.h"
+#include "..\vendor\nlohmann\json.hpp"
+
+#include <algorithm>
+
+namespace ServerRuntime
+{
+ namespace Access
+ {
+ using OrderedJson = nlohmann::ordered_json;
+
+ namespace
+ {
+ static const char *kWhitelistFileName = "whitelist.json";
+ }
+
+ WhitelistManager::WhitelistManager(const std::string &baseDirectory)
+ : m_baseDirectory(baseDirectory.empty() ? "." : baseDirectory)
+ {
+ }
+
+ bool WhitelistManager::EnsureWhitelistFileExists() const
+ {
+ const std::string path = GetWhitelistFilePath();
+ if (!AccessStorageUtils::EnsureJsonListFileExists(path))
+ {
+ LogErrorf("access", "failed to create %s", path.c_str());
+ return false;
+ }
+ return true;
+ }
+
+ bool WhitelistManager::Reload()
+ {
+ std::vector<WhitelistedPlayerEntry> players;
+ if (!LoadPlayers(&players))
+ {
+ return false;
+ }
+
+ m_whitelistedPlayers.swap(players);
+ return true;
+ }
+
+ bool WhitelistManager::Save() const
+ {
+ std::vector<WhitelistedPlayerEntry> players;
+ return SnapshotWhitelistedPlayers(&players) && SavePlayers(players);
+ }
+
+ bool WhitelistManager::LoadPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const
+ {
+ if (outEntries == nullptr)
+ {
+ return false;
+ }
+ outEntries->clear();
+
+ std::string text;
+ const std::string path = GetWhitelistFilePath();
+ if (!FileUtils::ReadTextFile(path, &text))
+ {
+ LogErrorf("access", "failed to read %s", path.c_str());
+ return false;
+ }
+
+ if (text.empty())
+ {
+ text = "[]";
+ }
+
+ OrderedJson root;
+ try
+ {
+ root = OrderedJson::parse(StringUtils::StripUtf8Bom(text));
+ }
+ catch (const nlohmann::json::exception &e)
+ {
+ LogErrorf("access", "failed to parse %s: %s", path.c_str(), e.what());
+ return false;
+ }
+
+ if (!root.is_array())
+ {
+ LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str());
+ return false;
+ }
+
+ for (const auto &object : root)
+ {
+ if (!object.is_object())
+ {
+ LogWarnf("access", "skipping whitelist entry that is not an object in %s", path.c_str());
+ continue;
+ }
+
+ std::string rawXuid;
+ if (!AccessStorageUtils::TryGetStringField(object, "xuid", &rawXuid))
+ {
+ LogWarnf("access", "skipping whitelist entry without xuid in %s", path.c_str());
+ continue;
+ }
+
+ WhitelistedPlayerEntry entry;
+ entry.xuid = AccessStorageUtils::NormalizeXuid(rawXuid);
+ if (entry.xuid.empty())
+ {
+ LogWarnf("access", "skipping whitelist entry with empty xuid in %s", path.c_str());
+ continue;
+ }
+
+ AccessStorageUtils::TryGetStringField(object, "name", &entry.name);
+ AccessStorageUtils::TryGetStringField(object, "created", &entry.metadata.created);
+ AccessStorageUtils::TryGetStringField(object, "source", &entry.metadata.source);
+ NormalizeMetadata(&entry.metadata);
+
+ outEntries->push_back(entry);
+ }
+
+ return true;
+ }
+
+ bool WhitelistManager::SavePlayers(const std::vector<WhitelistedPlayerEntry> &entries) const
+ {
+ OrderedJson root = OrderedJson::array();
+ for (const auto &entry : entries)
+ {
+ OrderedJson object = OrderedJson::object();
+ object["xuid"] = AccessStorageUtils::NormalizeXuid(entry.xuid);
+ object["name"] = entry.name;
+ object["created"] = entry.metadata.created;
+ object["source"] = entry.metadata.source;
+ root.push_back(object);
+ }
+
+ const std::string path = GetWhitelistFilePath();
+ const std::string json = root.empty() ? std::string("[]\n") : (root.dump(2) + "\n");
+ if (!FileUtils::WriteTextFileAtomic(path, json))
+ {
+ LogErrorf("access", "failed to write %s", path.c_str());
+ return false;
+ }
+ return true;
+ }
+
+ const std::vector<WhitelistedPlayerEntry> &WhitelistManager::GetWhitelistedPlayers() const
+ {
+ return m_whitelistedPlayers;
+ }
+
+ bool WhitelistManager::SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const
+ {
+ if (outEntries == nullptr)
+ {
+ return false;
+ }
+
+ *outEntries = m_whitelistedPlayers;
+ return true;
+ }
+
+ bool WhitelistManager::IsPlayerWhitelistedByXuid(const std::string &xuid) const
+ {
+ const auto normalized = AccessStorageUtils::NormalizeXuid(xuid);
+ if (normalized.empty())
+ {
+ return false;
+ }
+
+ return std::any_of(
+ m_whitelistedPlayers.begin(),
+ m_whitelistedPlayers.end(),
+ [&normalized](const WhitelistedPlayerEntry &entry)
+ {
+ return entry.xuid == normalized;
+ });
+ }
+
+ bool WhitelistManager::AddPlayer(const WhitelistedPlayerEntry &entry)
+ {
+ std::vector<WhitelistedPlayerEntry> updatedEntries;
+ if (!SnapshotWhitelistedPlayers(&updatedEntries))
+ {
+ return false;
+ }
+
+ auto normalized = entry;
+ normalized.xuid = AccessStorageUtils::NormalizeXuid(normalized.xuid);
+ NormalizeMetadata(&normalized.metadata);
+ if (normalized.xuid.empty())
+ {
+ return false;
+ }
+
+ const auto existing = std::find_if(
+ updatedEntries.begin(),
+ updatedEntries.end(),
+ [&normalized](const WhitelistedPlayerEntry &candidate)
+ {
+ return candidate.xuid == normalized.xuid;
+ });
+
+ if (existing != updatedEntries.end())
+ {
+ *existing = normalized;
+ if (!SavePlayers(updatedEntries))
+ {
+ return false;
+ }
+
+ m_whitelistedPlayers.swap(updatedEntries);
+ return true;
+ }
+
+ updatedEntries.push_back(normalized);
+ if (!SavePlayers(updatedEntries))
+ {
+ return false;
+ }
+
+ m_whitelistedPlayers.swap(updatedEntries);
+ return true;
+ }
+
+ bool WhitelistManager::RemovePlayerByXuid(const std::string &xuid)
+ {
+ const auto normalized = AccessStorageUtils::NormalizeXuid(xuid);
+ if (normalized.empty())
+ {
+ return false;
+ }
+
+ std::vector<WhitelistedPlayerEntry> updatedEntries;
+ if (!SnapshotWhitelistedPlayers(&updatedEntries))
+ {
+ return false;
+ }
+
+ const auto oldSize = updatedEntries.size();
+ updatedEntries.erase(
+ std::remove_if(
+ updatedEntries.begin(),
+ updatedEntries.end(),
+ [&normalized](const WhitelistedPlayerEntry &entry) { return entry.xuid == normalized; }),
+ updatedEntries.end());
+
+ if (updatedEntries.size() == oldSize)
+ {
+ return false;
+ }
+
+ if (!SavePlayers(updatedEntries))
+ {
+ return false;
+ }
+
+ m_whitelistedPlayers.swap(updatedEntries);
+ return true;
+ }
+
+ std::string WhitelistManager::GetWhitelistFilePath() const
+ {
+ return BuildPath(kWhitelistFileName);
+ }
+
+ WhitelistMetadata WhitelistManager::BuildDefaultMetadata(const char *source)
+ {
+ WhitelistMetadata metadata;
+ metadata.created = StringUtils::GetCurrentUtcTimestampIso8601();
+ metadata.source = (source != nullptr) ? source : "Server";
+ return metadata;
+ }
+
+ void WhitelistManager::NormalizeMetadata(WhitelistMetadata *metadata)
+ {
+ if (metadata == nullptr)
+ {
+ return;
+ }
+
+ metadata->created = StringUtils::TrimAscii(metadata->created);
+ metadata->source = StringUtils::TrimAscii(metadata->source);
+ }
+
+ std::string WhitelistManager::BuildPath(const char *fileName) const
+ {
+ return AccessStorageUtils::BuildPathFromBaseDirectory(m_baseDirectory, fileName);
+ }
+ }
+}
diff --git a/Minecraft.Server/Access/WhitelistManager.h b/Minecraft.Server/Access/WhitelistManager.h
new file mode 100644
index 00000000..1c2c5a0b
--- /dev/null
+++ b/Minecraft.Server/Access/WhitelistManager.h
@@ -0,0 +1,64 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace ServerRuntime
+{
+ namespace Access
+ {
+ struct WhitelistMetadata
+ {
+ std::string created;
+ std::string source;
+ };
+
+ struct WhitelistedPlayerEntry
+ {
+ std::string xuid;
+ std::string name;
+ WhitelistMetadata metadata;
+ };
+
+ /**
+ * whitelist manager
+ *
+ * Files:
+ * - whitelist.json
+ *
+ * Stores and normalizes XUID-based allow entries.
+ */
+ class WhitelistManager
+ {
+ public:
+ explicit WhitelistManager(const std::string &baseDirectory = ".");
+
+ bool EnsureWhitelistFileExists() const;
+ bool Reload();
+ bool Save() const;
+
+ bool LoadPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const;
+ bool SavePlayers(const std::vector<WhitelistedPlayerEntry> &entries) const;
+
+ const std::vector<WhitelistedPlayerEntry> &GetWhitelistedPlayers() const;
+ bool SnapshotWhitelistedPlayers(std::vector<WhitelistedPlayerEntry> *outEntries) const;
+
+ bool IsPlayerWhitelistedByXuid(const std::string &xuid) const;
+ bool AddPlayer(const WhitelistedPlayerEntry &entry);
+ bool RemovePlayerByXuid(const std::string &xuid);
+
+ std::string GetWhitelistFilePath() const;
+
+ static WhitelistMetadata BuildDefaultMetadata(const char *source = "Server");
+
+ private:
+ static void NormalizeMetadata(WhitelistMetadata *metadata);
+
+ std::string BuildPath(const char *fileName) const;
+
+ private:
+ std::string m_baseDirectory;
+ std::vector<WhitelistedPlayerEntry> m_whitelistedPlayers;
+ };
+ }
+}
diff --git a/Minecraft.Server/Common/AccessStorageUtils.h b/Minecraft.Server/Common/AccessStorageUtils.h
new file mode 100644
index 00000000..c5d3477c
--- /dev/null
+++ b/Minecraft.Server/Common/AccessStorageUtils.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include "FileUtils.h"
+#include "StringUtils.h"
+
+#include "..\vendor\nlohmann\json.hpp"
+
+#include <stdio.h>
+
+namespace ServerRuntime
+{
+ namespace AccessStorageUtils
+ {
+ inline bool IsRegularFile(const std::string &path)
+ {
+ const std::wstring widePath = StringUtils::Utf8ToWide(path);
+ if (widePath.empty())
+ {
+ return false;
+ }
+
+ const DWORD attributes = GetFileAttributesW(widePath.c_str());
+ return (attributes != INVALID_FILE_ATTRIBUTES) && ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
+ }
+
+ inline bool EnsureJsonListFileExists(const std::string &path)
+ {
+ return IsRegularFile(path) || FileUtils::WriteTextFileAtomic(path, "[]\n");
+ }
+
+ inline bool TryGetStringField(const nlohmann::ordered_json &object, const char *key, std::string *outValue)
+ {
+ if (key == nullptr || outValue == nullptr || !object.is_object())
+ {
+ return false;
+ }
+
+ const auto it = object.find(key);
+ if (it == object.end() || !it->is_string())
+ {
+ return false;
+ }
+
+ *outValue = it->get<std::string>();
+ return true;
+ }
+
+ inline std::string NormalizeXuid(const std::string &xuid)
+ {
+ const std::string trimmed = StringUtils::TrimAscii(xuid);
+ if (trimmed.empty())
+ {
+ return "";
+ }
+
+ unsigned long long numericXuid = 0;
+ if (StringUtils::TryParseUnsignedLongLong(trimmed, &numericXuid))
+ {
+ if (numericXuid == 0ULL)
+ {
+ return "";
+ }
+
+ char buffer[32] = {};
+ sprintf_s(buffer, sizeof(buffer), "0x%016llx", numericXuid);
+ return buffer;
+ }
+
+ return StringUtils::ToLowerAscii(trimmed);
+ }
+
+ inline std::string BuildPathFromBaseDirectory(const std::string &baseDirectory, const char *fileName)
+ {
+ if (fileName == nullptr || fileName[0] == 0)
+ {
+ return "";
+ }
+
+ const std::wstring wideFileName = StringUtils::Utf8ToWide(fileName);
+ if (wideFileName.empty())
+ {
+ return "";
+ }
+
+ if (baseDirectory.empty() || baseDirectory == ".")
+ {
+ return StringUtils::WideToUtf8(wideFileName);
+ }
+
+ const std::wstring wideBaseDirectory = StringUtils::Utf8ToWide(baseDirectory);
+ if (wideBaseDirectory.empty())
+ {
+ return StringUtils::WideToUtf8(wideFileName);
+ }
+
+ const wchar_t last = wideBaseDirectory[wideBaseDirectory.size() - 1];
+ if (last == L'\\' || last == L'/')
+ {
+ return StringUtils::WideToUtf8(wideBaseDirectory + wideFileName);
+ }
+
+ return StringUtils::WideToUtf8(wideBaseDirectory + L"\\" + wideFileName);
+ }
+ }
+}
diff --git a/Minecraft.Server/Common/FileUtils.cpp b/Minecraft.Server/Common/FileUtils.cpp
new file mode 100644
index 00000000..50eeeb72
--- /dev/null
+++ b/Minecraft.Server/Common/FileUtils.cpp
@@ -0,0 +1,146 @@
+#include "stdafx.h"
+
+#include "FileUtils.h"
+
+#include "StringUtils.h"
+
+#include <io.h>
+#include <stdio.h>
+
+namespace ServerRuntime
+{
+ namespace FileUtils
+ {
+ namespace
+ {
+ static std::wstring ToWidePath(const std::string &filePath)
+ {
+ return StringUtils::Utf8ToWide(filePath);
+ }
+ }
+
+ unsigned long long GetCurrentUtcFileTime()
+ {
+ FILETIME now = {};
+ GetSystemTimeAsFileTime(&now);
+
+ ULARGE_INTEGER value = {};
+ value.LowPart = now.dwLowDateTime;
+ value.HighPart = now.dwHighDateTime;
+ return value.QuadPart;
+ }
+
+ bool ReadTextFile(const std::string &filePath, std::string *outText)
+ {
+ if (outText == nullptr)
+ {
+ return false;
+ }
+
+ outText->clear();
+
+ const std::wstring widePath = ToWidePath(filePath);
+ if (widePath.empty())
+ {
+ return false;
+ }
+
+ FILE *inFile = nullptr;
+ if (_wfopen_s(&inFile, widePath.c_str(), L"rb") != 0 || inFile == nullptr)
+ {
+ return false;
+ }
+
+ if (fseek(inFile, 0, SEEK_END) != 0)
+ {
+ fclose(inFile);
+ return false;
+ }
+
+ long fileSize = ftell(inFile);
+ if (fileSize < 0)
+ {
+ fclose(inFile);
+ return false;
+ }
+
+ if (fseek(inFile, 0, SEEK_SET) != 0)
+ {
+ fclose(inFile);
+ return false;
+ }
+
+ if (fileSize == 0)
+ {
+ fclose(inFile);
+ return true;
+ }
+
+ outText->resize((size_t)fileSize);
+ size_t bytesRead = fread(&(*outText)[0], 1, (size_t)fileSize, inFile);
+ fclose(inFile);
+
+ if (bytesRead != (size_t)fileSize)
+ {
+ outText->clear();
+ return false;
+ }
+
+ return true;
+ }
+
+ bool WriteTextFileAtomic(const std::string &filePath, const std::string &text)
+ {
+ const std::wstring widePath = ToWidePath(filePath);
+ if (widePath.empty())
+ {
+ return false;
+ }
+
+ const std::wstring tmpPath = widePath + L".tmp";
+
+ FILE *outFile = nullptr;
+ if (_wfopen_s(&outFile, tmpPath.c_str(), L"wb") != 0 || outFile == nullptr)
+ {
+ return false;
+ }
+
+ if (!text.empty())
+ {
+ size_t bytesWritten = fwrite(text.data(), 1, text.size(), outFile);
+ if (bytesWritten != text.size())
+ {
+ fclose(outFile);
+ DeleteFileW(tmpPath.c_str());
+ return false;
+ }
+ }
+
+ if (fflush(outFile) != 0 || _commit(_fileno(outFile)) != 0)
+ {
+ fclose(outFile);
+ DeleteFileW(tmpPath.c_str());
+ return false;
+ }
+ fclose(outFile);
+
+ DWORD attrs = GetFileAttributesW(widePath.c_str());
+ if (attrs != INVALID_FILE_ATTRIBUTES && ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0))
+ {
+ // Replace the destination without deleting the last known-good file first.
+ if (ReplaceFileW(widePath.c_str(), tmpPath.c_str(), nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS, nullptr, nullptr))
+ {
+ return true;
+ }
+ }
+
+ if (MoveFileExW(tmpPath.c_str(), widePath.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
+ {
+ return true;
+ }
+
+ // Keep the temp file on failure so the original file remains recoverable and the caller can inspect the write result.
+ return false;
+ }
+ }
+} \ No newline at end of file
diff --git a/Minecraft.Server/Common/FileUtils.h b/Minecraft.Server/Common/FileUtils.h
new file mode 100644
index 00000000..96e398cc
--- /dev/null
+++ b/Minecraft.Server/Common/FileUtils.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <string>
+
+namespace ServerRuntime
+{
+ namespace FileUtils
+ {
+ /**
+ * Reads the full UTF-8 path target into memory without interpreting JSON or line endings
+ * UTF-8パスのテキストファイル全体をそのまま読み込む
+ */
+ bool ReadTextFile(const std::string &filePath, std::string *outText);
+ /**
+ * Writes text through a same-directory temporary file and publishes it with a single replacement step
+ * 同一ディレクトリの一時ファイル経由で安全に書き換える
+ */
+ bool WriteTextFileAtomic(const std::string &filePath, const std::string &text);
+ /**
+ * Returns the current UTC timestamp encoded in Windows FILETIME units for expiry comparisons
+ * 期限判定用に現在UTC時刻をWindows FILETIME単位で返す
+ */
+ unsigned long long GetCurrentUtcFileTime();
+ }
+} \ No newline at end of file
diff --git a/Minecraft.Server/Common/NetworkUtils.h b/Minecraft.Server/Common/NetworkUtils.h
new file mode 100644
index 00000000..c77d4506
--- /dev/null
+++ b/Minecraft.Server/Common/NetworkUtils.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "StringUtils.h"
+
+#include <WS2tcpip.h>
+
+namespace ServerRuntime
+{
+ namespace NetworkUtils
+ {
+ inline std::string NormalizeIpToken(const std::string &ip)
+ {
+ return StringUtils::ToLowerAscii(StringUtils::TrimAscii(ip));
+ }
+
+ inline bool IsIpLiteral(const std::string &text)
+ {
+ const std::string trimmed = StringUtils::TrimAscii(text);
+ if (trimmed.empty())
+ {
+ return false;
+ }
+
+ IN_ADDR ipv4 = {};
+ IN6_ADDR ipv6 = {};
+ return InetPtonA(AF_INET, trimmed.c_str(), &ipv4) == 1 || InetPtonA(AF_INET6, trimmed.c_str(), &ipv6) == 1;
+ }
+ }
+}
diff --git a/Minecraft.Server/Common/StringUtils.cpp b/Minecraft.Server/Common/StringUtils.cpp
new file mode 100644
index 00000000..40881ae7
--- /dev/null
+++ b/Minecraft.Server/Common/StringUtils.cpp
@@ -0,0 +1,212 @@
+#include "stdafx.h"
+
+#include "StringUtils.h"
+
+#include <cctype>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace ServerRuntime
+{
+ namespace StringUtils
+ {
+ std::string WideToUtf8(const std::wstring &value)
+ {
+ if (value.empty())
+ {
+ return std::string();
+ }
+
+ int charCount = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.length(), NULL, 0, NULL, NULL);
+ if (charCount <= 0)
+ {
+ return std::string();
+ }
+
+ std::string utf8;
+ utf8.resize(charCount);
+ WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.length(), &utf8[0], charCount, NULL, NULL);
+ return utf8;
+ }
+
+ std::wstring Utf8ToWide(const char *value)
+ {
+ if (value == NULL || value[0] == 0)
+ {
+ return std::wstring();
+ }
+
+ int wideCount = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0);
+ if (wideCount <= 0)
+ {
+ // Fall back to the current ANSI code page so legacy non-UTF-8 inputs remain readable.
+ wideCount = MultiByteToWideChar(CP_ACP, 0, value, -1, NULL, 0);
+ if (wideCount <= 0)
+ {
+ return std::wstring();
+ }
+
+ std::wstring wide;
+ wide.resize(wideCount - 1);
+ MultiByteToWideChar(CP_ACP, 0, value, -1, &wide[0], wideCount);
+ return wide;
+ }
+
+ std::wstring wide;
+ wide.resize(wideCount - 1);
+ MultiByteToWideChar(CP_UTF8, 0, value, -1, &wide[0], wideCount);
+ return wide;
+ }
+
+ std::wstring Utf8ToWide(const std::string &value)
+ {
+ return Utf8ToWide(value.c_str());
+ }
+
+ std::string StripUtf8Bom(const std::string &value)
+ {
+ if (value.size() >= 3 &&
+ (unsigned char)value[0] == 0xEF &&
+ (unsigned char)value[1] == 0xBB &&
+ (unsigned char)value[2] == 0xBF)
+ {
+ return value.substr(3);
+ }
+
+ return value;
+ }
+
+ std::string TrimAscii(const std::string &value)
+ {
+ size_t start = 0;
+ while (start < value.length() && std::isspace((unsigned char)value[start]))
+ {
+ ++start;
+ }
+
+ size_t end = value.length();
+ while (end > start && std::isspace((unsigned char)value[end - 1]))
+ {
+ --end;
+ }
+
+ return value.substr(start, end - start);
+ }
+
+ std::string ToLowerAscii(const std::string &value)
+ {
+ std::string lowered = value;
+ for (size_t i = 0; i < lowered.length(); ++i)
+ {
+ lowered[i] = (char)std::tolower((unsigned char)lowered[i]);
+ }
+ return lowered;
+ }
+
+ std::string JoinTokens(const std::vector<std::string> &tokens, size_t startIndex, const char *separator)
+ {
+ if (startIndex >= tokens.size())
+ {
+ return std::string();
+ }
+
+ const auto joinSeparator = std::string((separator != nullptr) ? separator : " ");
+ size_t totalLength = 0;
+ for (size_t i = startIndex; i < tokens.size(); ++i)
+ {
+ totalLength += tokens[i].size();
+ }
+
+ totalLength += (tokens.size() - startIndex - 1) * joinSeparator.size();
+ std::string joined;
+ joined.reserve(totalLength);
+ for (size_t i = startIndex; i < tokens.size(); ++i)
+ {
+ if (!joined.empty())
+ {
+ joined += joinSeparator;
+ }
+
+ joined += tokens[i];
+ }
+
+ return joined;
+ }
+
+ bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix)
+ {
+ if (prefix.size() > value.size())
+ {
+ return false;
+ }
+
+ for (size_t i = 0; i < prefix.size(); ++i)
+ {
+ unsigned char a = (unsigned char)value[i];
+ unsigned char b = (unsigned char)prefix[i];
+ if (std::tolower(a) != std::tolower(b))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool TryParseUnsignedLongLong(const std::string &value, unsigned long long *outValue)
+ {
+ if (outValue == nullptr)
+ {
+ return false;
+ }
+
+ const std::string trimmed = TrimAscii(value);
+ if (trimmed.empty())
+ {
+ return false;
+ }
+
+ errno = 0;
+ char *end = nullptr;
+ const unsigned long long parsed = _strtoui64(trimmed.c_str(), &end, 0);
+ if (end == trimmed.c_str() || errno != 0)
+ {
+ return false;
+ }
+
+ while (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n')
+ {
+ ++end;
+ }
+
+ if (*end != 0)
+ {
+ return false;
+ }
+
+ *outValue = parsed;
+ return true;
+ }
+
+ std::string GetCurrentUtcTimestampIso8601()
+ {
+ SYSTEMTIME utc = {};
+ GetSystemTime(&utc);
+
+ char created[64] = {};
+ sprintf_s(
+ created,
+ sizeof(created),
+ "%04u-%02u-%02uT%02u:%02u:%02uZ",
+ (unsigned)utc.wYear,
+ (unsigned)utc.wMonth,
+ (unsigned)utc.wDay,
+ (unsigned)utc.wHour,
+ (unsigned)utc.wMinute,
+ (unsigned)utc.wSecond);
+ return created;
+ }
+ }
+}
+
diff --git a/Minecraft.Server/Common/StringUtils.h b/Minecraft.Server/Common/StringUtils.h
new file mode 100644
index 00000000..68f818e1
--- /dev/null
+++ b/Minecraft.Server/Common/StringUtils.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace ServerRuntime
+{
+ namespace StringUtils
+ {
+ std::string WideToUtf8(const std::wstring &value);
+ std::wstring Utf8ToWide(const char *value);
+ std::wstring Utf8ToWide(const std::string &value);
+ std::string StripUtf8Bom(const std::string &value);
+
+ std::string TrimAscii(const std::string &value);
+ std::string ToLowerAscii(const std::string &value);
+ std::string JoinTokens(const std::vector<std::string> &tokens, size_t startIndex = 0, const char *separator = " ");
+ bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix);
+ bool TryParseUnsignedLongLong(const std::string &value, unsigned long long *outValue);
+ std::string GetCurrentUtcTimestampIso8601();
+ }
+}
+
diff --git a/Minecraft.Server/Console/ServerCli.cpp b/Minecraft.Server/Console/ServerCli.cpp
new file mode 100644
index 00000000..b633effd
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCli.cpp
@@ -0,0 +1,44 @@
+#include "stdafx.h"
+
+#include "ServerCli.h"
+
+#include "ServerCliEngine.h"
+#include "ServerCliInput.h"
+
+namespace ServerRuntime
+{
+ ServerCli::ServerCli()
+ : m_engine(new ServerCliEngine())
+ , m_input(new ServerCliInput())
+ {
+ }
+
+ ServerCli::~ServerCli()
+ {
+ Stop();
+ }
+
+ void ServerCli::Start()
+ {
+ if (m_input && m_engine)
+ {
+ m_input->Start(m_engine.get());
+ }
+ }
+
+ void ServerCli::Stop()
+ {
+ if (m_input)
+ {
+ m_input->Stop();
+ }
+ }
+
+ void ServerCli::Poll()
+ {
+ if (m_engine)
+ {
+ m_engine->Poll();
+ }
+ }
+}
diff --git a/Minecraft.Server/Console/ServerCli.h b/Minecraft.Server/Console/ServerCli.h
new file mode 100644
index 00000000..f544450b
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCli.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <memory>
+
+namespace ServerRuntime
+{
+ class ServerCliEngine;
+ class ServerCliInput;
+
+ /**
+ * **Server CLI facade**
+ *
+ * Owns the command engine and input component, and exposes a small lifecycle API.
+ * CLI 全体の開始・停止・更新をまとめる窓口クラス
+ */
+ class ServerCli
+ {
+ public:
+ ServerCli();
+ ~ServerCli();
+
+ /**
+ * **Start console input processing**
+ *
+ * Connects input to the engine and starts background reading.
+ * 入力処理を開始してエンジンに接続
+ */
+ void Start();
+
+ /**
+ * **Stop console input processing**
+ *
+ * Stops background input safely and detaches from the engine.
+ * 入力処理を安全に停止
+ */
+ void Stop();
+
+ /**
+ * **Process queued command lines**
+ *
+ * Drains commands collected by input and executes them in the main loop.
+ * 入力キューのコマンドを実行
+ */
+ void Poll();
+
+ private:
+ std::unique_ptr<ServerCliEngine> m_engine;
+ std::unique_ptr<ServerCliInput> m_input;
+ };
+}
diff --git a/Minecraft.Server/Console/ServerCliEngine.cpp b/Minecraft.Server/Console/ServerCliEngine.cpp
new file mode 100644
index 00000000..82bbdcc8
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCliEngine.cpp
@@ -0,0 +1,395 @@
+#include "stdafx.h"
+
+#include "ServerCliEngine.h"
+
+#include "ServerCliParser.h"
+#include "ServerCliRegistry.h"
+#include "commands\IServerCliCommand.h"
+#include "commands\ban\CliCommandBan.h"
+#include "commands\ban-ip\CliCommandBanIp.h"
+#include "commands\ban-list\CliCommandBanList.h"
+#include "commands\defaultgamemode\CliCommandDefaultGamemode.h"
+#include "commands\enchant\CliCommandEnchant.h"
+#include "commands\experience\CliCommandExperience.h"
+#include "commands\gamemode\CliCommandGamemode.h"
+#include "commands\give\CliCommandGive.h"
+#include "commands\help\CliCommandHelp.h"
+#include "commands\kill\CliCommandKill.h"
+#include "commands\list\CliCommandList.h"
+#include "commands\pardon\CliCommandPardon.h"
+#include "commands\pardon-ip\CliCommandPardonIp.h"
+#include "commands\stop\CliCommandStop.h"
+#include "commands\time\CliCommandTime.h"
+#include "commands\tp\CliCommandTp.h"
+#include "commands\weather\CliCommandWeather.h"
+#include "commands\whitelist\CliCommandWhitelist.h"
+#include "..\Common\StringUtils.h"
+#include "..\ServerShutdown.h"
+#include "..\ServerLogger.h"
+#include "..\..\Minecraft.Client\MinecraftServer.h"
+#include "..\..\Minecraft.Client\PlayerList.h"
+#include "..\..\Minecraft.Client\ServerPlayer.h"
+#include "..\..\Minecraft.World\CommandDispatcher.h"
+#include "..\..\Minecraft.World\CommandSender.h"
+#include "..\..\Minecraft.World\LevelSettings.h"
+#include "..\..\Minecraft.World\StringHelpers.h"
+
+#include <stdlib.h>
+#include <unordered_set>
+
+namespace ServerRuntime
+{
+
+ /**
+ * Create an authorized Sender to make the CLI appear as a user.
+ * The return value can also be used to display logs.
+ */
+ namespace
+ {
+ class ServerCliConsoleCommandSender : public CommandSender
+ {
+ public:
+ explicit ServerCliConsoleCommandSender(const ServerCliEngine *engine)
+ : m_engine(engine)
+ {
+ }
+
+ void sendMessage(const wstring &message, ChatPacket::EChatPacketMessage type, int customData, const wstring &additionalMessage) override
+ {
+ (void)type;
+ (void)customData;
+ (void)additionalMessage;
+ if (m_engine == nullptr)
+ {
+ return;
+ }
+
+ m_engine->LogInfo(StringUtils::WideToUtf8(message));
+ }
+
+ bool hasPermission(EGameCommand command) override
+ {
+ (void)command;
+ return true;
+ }
+
+ private:
+ const ServerCliEngine *m_engine;
+ };
+ }
+
+ ServerCliEngine::ServerCliEngine()
+ : m_registry(new ServerCliRegistry())
+ , m_consoleSender(std::make_shared<ServerCliConsoleCommandSender>(this))
+ {
+ RegisterDefaultCommands();
+ }
+
+ ServerCliEngine::~ServerCliEngine()
+ {
+ }
+
+ void ServerCliEngine::RegisterDefaultCommands()
+ {
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandHelp()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandStop()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandList()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandBan()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandBanIp()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandPardon()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandPardonIp()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandBanList()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandWhitelist()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandTp()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandTime()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandWeather()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandGive()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandEnchant()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandKill()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandGamemode()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandDefaultGamemode()));
+ m_registry->Register(std::unique_ptr<IServerCliCommand>(new CliCommandExperience()));
+ }
+
+ void ServerCliEngine::EnqueueCommandLine(const std::string &line)
+ {
+ std::lock_guard<std::mutex> lock(m_queueMutex);
+ m_pendingLines.push(line);
+ }
+
+ void ServerCliEngine::Poll()
+ {
+ for (;;)
+ {
+ std::string line;
+ {
+ // Keep the lock scope minimal: dequeue only, execute outside.
+ std::lock_guard<std::mutex> lock(m_queueMutex);
+ if (m_pendingLines.empty())
+ {
+ break;
+ }
+ line = m_pendingLines.front();
+ m_pendingLines.pop();
+ }
+
+ ExecuteCommandLine(line);
+ }
+ }
+
+ bool ServerCliEngine::ExecuteCommandLine(const std::string &line)
+ {
+ // Normalize user input before parsing (trim + optional leading slash).
+ std::wstring wide = trimString(StringUtils::Utf8ToWide(line));
+ if (wide.empty())
+ {
+ return true;
+ }
+
+ std::string normalizedLine = StringUtils::WideToUtf8(wide);
+ if (!normalizedLine.empty() && normalizedLine[0] == '/')
+ {
+ normalizedLine = normalizedLine.substr(1);
+ }
+
+ ServerCliParsedLine parsed = ServerCliParser::Parse(normalizedLine);
+ if (parsed.tokens.empty())
+ {
+ return true;
+ }
+
+ IServerCliCommand *command = m_registry->FindMutable(parsed.tokens[0]);
+ if (command == NULL)
+ {
+ LogWarn("Unknown command: " + parsed.tokens[0]);
+ return false;
+ }
+
+ return command->Execute(parsed, this);
+ }
+
+ void ServerCliEngine::BuildCompletions(const std::string &line, std::vector<std::string> *out) const
+ {
+ if (out == NULL)
+ {
+ return;
+ }
+
+ out->clear();
+ ServerCliCompletionContext context = ServerCliParser::BuildCompletionContext(line);
+ bool slashPrefixedCommand = false;
+ std::string commandToken;
+ if (!context.parsed.tokens.empty())
+ {
+ // Completion accepts both "tp" and "/tp" style command heads.
+ commandToken = context.parsed.tokens[0];
+ if (!commandToken.empty() && commandToken[0] == '/')
+ {
+ commandToken = commandToken.substr(1);
+ slashPrefixedCommand = true;
+ }
+ }
+
+ if (context.currentTokenIndex == 0)
+ {
+ std::string prefix = context.prefix;
+ if (!prefix.empty() && prefix[0] == '/')
+ {
+ prefix = prefix.substr(1);
+ slashPrefixedCommand = true;
+ }
+
+ std::string linePrefix = context.linePrefix;
+ if (slashPrefixedCommand && linePrefix.empty())
+ {
+ // Preserve leading slash when user started with "/".
+ linePrefix = "/";
+ }
+
+ m_registry->SuggestCommandNames(prefix, linePrefix, out);
+ }
+ else
+ {
+ const IServerCliCommand *command = m_registry->Find(commandToken);
+ if (command != NULL)
+ {
+ command->Complete(context, this, out);
+ }
+ }
+
+ std::unordered_set<std::string> seen;
+ std::vector<std::string> unique;
+ for (size_t i = 0; i < out->size(); ++i)
+ {
+ // Remove duplicates while keeping first-seen ordering.
+ if (seen.insert((*out)[i]).second)
+ {
+ unique.push_back((*out)[i]);
+ }
+ }
+ out->swap(unique);
+ }
+
+ void ServerCliEngine::LogInfo(const std::string &message) const
+ {
+ LogInfof("console", "%s", message.c_str());
+ }
+
+ void ServerCliEngine::LogWarn(const std::string &message) const
+ {
+ LogWarnf("console", "%s", message.c_str());
+ }
+
+ void ServerCliEngine::LogError(const std::string &message) const
+ {
+ LogErrorf("console", "%s", message.c_str());
+ }
+
+ void ServerCliEngine::RequestShutdown() const
+ {
+ RequestDedicatedServerShutdown();
+ }
+
+ std::vector<std::string> ServerCliEngine::GetOnlinePlayerNamesUtf8() const
+ {
+ std::vector<std::string> result;
+ MinecraftServer *server = MinecraftServer::getInstance();
+ if (server == NULL)
+ {
+ return result;
+ }
+
+ PlayerList *players = server->getPlayers();
+ if (players == NULL)
+ {
+ return result;
+ }
+
+ for (size_t i = 0; i < players->players.size(); ++i)
+ {
+ std::shared_ptr<ServerPlayer> player = players->players[i];
+ if (player != NULL)
+ {
+ result.push_back(StringUtils::WideToUtf8(player->getName()));
+ }
+ }
+
+ return result;
+ }
+
+ std::shared_ptr<ServerPlayer> ServerCliEngine::FindPlayerByNameUtf8(const std::string &name) const
+ {
+ MinecraftServer *server = MinecraftServer::getInstance();
+ if (server == NULL)
+ {
+ return nullptr;
+ }
+
+ PlayerList *players = server->getPlayers();
+ if (players == NULL)
+ {
+ return nullptr;
+ }
+
+ std::wstring target = StringUtils::Utf8ToWide(name);
+ for (size_t i = 0; i < players->players.size(); ++i)
+ {
+ std::shared_ptr<ServerPlayer> player = players->players[i];
+ if (player != NULL && equalsIgnoreCase(player->getName(), target))
+ {
+ return player;
+ }
+ }
+
+ return nullptr;
+ }
+
+ void ServerCliEngine::SuggestPlayers(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const
+ {
+ std::vector<std::string> players = GetOnlinePlayerNamesUtf8();
+ std::string loweredPrefix = StringUtils::ToLowerAscii(prefix);
+ for (size_t i = 0; i < players.size(); ++i)
+ {
+ std::string loweredName = StringUtils::ToLowerAscii(players[i]);
+ if (loweredName.compare(0, loweredPrefix.size(), loweredPrefix) == 0)
+ {
+ out->push_back(linePrefix + players[i]);
+ }
+ }
+ }
+
+ void ServerCliEngine::SuggestGamemodes(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const
+ {
+ static const char *kModes[] = { "survival", "creative", "s", "c", "0", "1" };
+ std::string loweredPrefix = StringUtils::ToLowerAscii(prefix);
+ for (size_t i = 0; i < sizeof(kModes) / sizeof(kModes[0]); ++i)
+ {
+ std::string candidate = kModes[i];
+ std::string loweredCandidate = StringUtils::ToLowerAscii(candidate);
+ if (loweredCandidate.compare(0, loweredPrefix.size(), loweredPrefix) == 0)
+ {
+ out->push_back(linePrefix + candidate);
+ }
+ }
+ }
+
+ GameType *ServerCliEngine::ParseGamemode(const std::string &token) const
+ {
+ std::string lowered = StringUtils::ToLowerAscii(token);
+ if (lowered == "survival" || lowered == "s" || lowered == "0")
+ {
+ return GameType::SURVIVAL;
+ }
+ if (lowered == "creative" || lowered == "c" || lowered == "1")
+ {
+ return GameType::CREATIVE;
+ }
+
+ char *end = NULL;
+ long id = strtol(lowered.c_str(), &end, 10);
+ if (end != NULL && *end == 0)
+ {
+ // Numeric fallback supports extended ids handled by level settings.
+ return LevelSettings::validateGameType((int)id);
+ }
+
+ return NULL;
+ }
+
+ bool ServerCliEngine::DispatchWorldCommand(EGameCommand command, byteArray commandData, const std::shared_ptr<CommandSender> &sender) const
+ {
+ MinecraftServer *server = MinecraftServer::getInstance();
+ if (server == NULL)
+ {
+ LogWarn("MinecraftServer instance is not available.");
+ return false;
+ }
+
+ CommandDispatcher *dispatcher = server->getCommandDispatcher();
+ if (dispatcher == NULL)
+ {
+ LogWarn("Command dispatcher is not available.");
+ return false;
+ }
+
+ std::shared_ptr<CommandSender> commandSender = sender;
+ if (commandSender == nullptr)
+ {
+ // fall back to console sender if caller did not provide one
+ commandSender = m_consoleSender;
+ }
+ if (commandSender == nullptr)
+ {
+ LogWarn("No command sender is available.");
+ return false;
+ }
+
+ dispatcher->performCommand(commandSender, command, commandData);
+ return true;
+ }
+
+ const ServerCliRegistry &ServerCliEngine::Registry() const
+ {
+ return *m_registry;
+ }
+}
diff --git a/Minecraft.Server/Console/ServerCliEngine.h b/Minecraft.Server/Console/ServerCliEngine.h
new file mode 100644
index 00000000..b2d72bac
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCliEngine.h
@@ -0,0 +1,127 @@
+#pragma once
+
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "..\..\Minecraft.World\ArrayWithLength.h"
+#include "..\..\Minecraft.World\CommandsEnum.h"
+
+class GameType;
+class ServerPlayer;
+class CommandSender;
+
+namespace ServerRuntime
+{
+ class ServerCliRegistry;
+
+ /**
+ * **CLI execution engine**
+ *
+ * Handles parsing, command dispatch, completion suggestions, and server-side helpers.
+ * 解析・実行・補完エンジン
+ */
+ class ServerCliEngine
+ {
+ public:
+ ServerCliEngine();
+ ~ServerCliEngine();
+
+ /**
+ * **Queue one raw command line**
+ *
+ * Called by input thread; execution is deferred to `Poll()`.
+ * 入力行を実行キューに追加
+ */
+ void EnqueueCommandLine(const std::string &line);
+
+ /**
+ * **Execute queued commands**
+ *
+ * Drains pending lines and dispatches them in order.
+ * キュー済みコマンドを順番に実行
+ */
+ void Poll();
+
+ /**
+ * **Execute one command line immediately**
+ *
+ * Parses and dispatches a normalized line to a registered command.
+ * 1行を直接パースしてコマンド実行
+ */
+ bool ExecuteCommandLine(const std::string &line);
+
+ /**
+ * **Build completion candidates for current line**
+ *
+ * Produces command or argument suggestions based on parser context.
+ * 現在入力に対する補完候補を作成
+ */
+ void BuildCompletions(const std::string &line, std::vector<std::string> *out) const;
+
+ void LogInfo(const std::string &message) const;
+ void LogWarn(const std::string &message) const;
+ void LogError(const std::string &message) const;
+ void RequestShutdown() const;
+
+ /**
+ * **List connected players as UTF-8 names**
+ *
+ * ここら辺は分けてもいいかも
+ */
+ std::vector<std::string> GetOnlinePlayerNamesUtf8() const;
+
+ /**
+ * **Find a player by UTF-8 name**
+ */
+ std::shared_ptr<ServerPlayer> FindPlayerByNameUtf8(const std::string &name) const;
+
+ /**
+ * **Suggest player-name arguments**
+ *
+ * Appends matching player candidates using the given completion prefix.
+ * プレイヤー名の補完候補
+ */
+ void SuggestPlayers(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const;
+
+ /**
+ * **Suggest gamemode arguments**
+ *
+ * Appends standard gamemode aliases (survival/creative/0/1).
+ * ゲームモードの補完候補
+ */
+ void SuggestGamemodes(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const;
+
+ /**
+ * **Parse gamemode token**
+ *
+ * Supports names, short aliases, and numeric ids.
+ * 文字列からゲームモードを解決
+ */
+ GameType *ParseGamemode(const std::string &token) const;
+
+ /**
+ * **Dispatch one Minecraft.World game command**
+ *
+ * Uses `Minecraft.World::CommandDispatcher` for actual execution.
+ * When `sender` is null, an internal console command sender is used.
+ *
+ * Minecraft.Worldのコマンドを実行するためのディスパッチャーのラッパー
+ * 内部でsenderがnullの場合はコンソールコマンド送信者を使用
+ */
+ bool DispatchWorldCommand(EGameCommand command, byteArray commandData, const std::shared_ptr<CommandSender> &sender = nullptr) const;
+
+ const ServerCliRegistry &Registry() const;
+
+ private:
+ void RegisterDefaultCommands();
+
+ private:
+ mutable std::mutex m_queueMutex;
+ std::queue<std::string> m_pendingLines;
+ std::unique_ptr<ServerCliRegistry> m_registry;
+ std::shared_ptr<CommandSender> m_consoleSender;
+ };
+}
diff --git a/Minecraft.Server/Console/ServerCliInput.cpp b/Minecraft.Server/Console/ServerCliInput.cpp
new file mode 100644
index 00000000..d873980a
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCliInput.cpp
@@ -0,0 +1,285 @@
+#include "stdafx.h"
+
+#include "ServerCliInput.h"
+
+#include "ServerCliEngine.h"
+#include "..\ServerLogger.h"
+#include "..\vendor\linenoise\linenoise.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+
+namespace
+{
+ bool UseStreamInputMode()
+ {
+ const char *mode = getenv("SERVER_CLI_INPUT_MODE");
+ if (mode != NULL)
+ {
+ return _stricmp(mode, "stream") == 0
+ || _stricmp(mode, "stdin") == 0;
+ }
+
+ return false;
+ }
+
+ int WaitForStdinReadable(HANDLE stdinHandle, DWORD waitMs)
+ {
+ if (stdinHandle == NULL || stdinHandle == INVALID_HANDLE_VALUE)
+ {
+ return -1;
+ }
+
+ DWORD fileType = GetFileType(stdinHandle);
+ if (fileType == FILE_TYPE_PIPE)
+ {
+ DWORD available = 0;
+ if (!PeekNamedPipe(stdinHandle, NULL, 0, NULL, &available, NULL))
+ {
+ return -1;
+ }
+ return available > 0 ? 1 : 0;
+ }
+
+ if (fileType == FILE_TYPE_CHAR)
+ {
+ // console/pty char handles are often not waitable across Wine+Docker.
+ return 1;
+ }
+
+ DWORD waitResult = WaitForSingleObject(stdinHandle, waitMs);
+ if (waitResult == WAIT_OBJECT_0)
+ {
+ return 1;
+ }
+ if (waitResult == WAIT_TIMEOUT)
+ {
+ return 0;
+ }
+
+ return -1;
+ }
+}
+
+namespace ServerRuntime
+{
+ // C-style completion callback bridge requires a static instance pointer.
+ ServerCliInput *ServerCliInput::s_instance = NULL;
+
+ ServerCliInput::ServerCliInput()
+ : m_running(false)
+ , m_engine(NULL)
+ {
+ }
+
+ ServerCliInput::~ServerCliInput()
+ {
+ Stop();
+ }
+
+ void ServerCliInput::Start(ServerCliEngine *engine)
+ {
+ if (engine == NULL || m_running.exchange(true))
+ {
+ return;
+ }
+
+ m_engine = engine;
+ s_instance = this;
+ linenoiseResetStop();
+ linenoiseHistorySetMaxLen(128);
+ linenoiseSetCompletionCallback(&ServerCliInput::CompletionThunk);
+ m_inputThread = std::thread(&ServerCliInput::RunInputLoop, this);
+ LogInfo("console", "CLI input thread started.");
+ }
+
+ void ServerCliInput::Stop()
+ {
+ if (!m_running.exchange(false))
+ {
+ return;
+ }
+
+ // Ask linenoise to break out first, then join thread safely.
+ linenoiseRequestStop();
+ if (m_inputThread.joinable())
+ {
+ CancelSynchronousIo((HANDLE)m_inputThread.native_handle());
+ m_inputThread.join();
+ }
+ linenoiseSetCompletionCallback(NULL);
+
+ if (s_instance == this)
+ {
+ s_instance = NULL;
+ }
+
+ m_engine = NULL;
+ LogInfo("console", "CLI input thread stopped.");
+ }
+
+ bool ServerCliInput::IsRunning() const
+ {
+ return m_running.load();
+ }
+
+ void ServerCliInput::RunInputLoop()
+ {
+ if (UseStreamInputMode())
+ {
+ LogInfo("console", "CLI input mode: stream(file stdin)");
+ RunStreamInputLoop();
+ return;
+ }
+
+ RunLinenoiseLoop();
+ }
+
+ /**
+ * use linenoise for interactive console input, with line editing and history support
+ */
+ void ServerCliInput::RunLinenoiseLoop()
+ {
+ while (m_running)
+ {
+ char *line = linenoise("server> ");
+ if (line == NULL)
+ {
+ // NULL is expected on stop request (or Ctrl+C inside linenoise).
+ if (!m_running)
+ {
+ break;
+ }
+ Sleep(10);
+ continue;
+ }
+
+ EnqueueLine(line);
+
+ linenoiseFree(line);
+ }
+ }
+
+ /**
+ * use file-based stdin reading instead of linenoise when requested or when stdin is not a console/pty (e.g. piped input or non-interactive docker)
+ */
+ void ServerCliInput::RunStreamInputLoop()
+ {
+ HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
+ if (stdinHandle == NULL || stdinHandle == INVALID_HANDLE_VALUE)
+ {
+ LogWarn("console", "stream input mode requested but STDIN handle is unavailable; falling back to linenoise.");
+ RunLinenoiseLoop();
+ return;
+ }
+
+ std::string line;
+ bool skipNextLf = false;
+
+ printf("server> ");
+ fflush(stdout);
+
+ while (m_running)
+ {
+ int readable = WaitForStdinReadable(stdinHandle, 50);
+ if (readable <= 0)
+ {
+ Sleep(10);
+ continue;
+ }
+
+ char ch = 0;
+ DWORD bytesRead = 0;
+ if (!ReadFile(stdinHandle, &ch, 1, &bytesRead, NULL) || bytesRead == 0)
+ {
+ Sleep(10);
+ continue;
+ }
+
+ if (skipNextLf && ch == '\n')
+ {
+ skipNextLf = false;
+ continue;
+ }
+
+ if (ch == '\r' || ch == '\n')
+ {
+ if (ch == '\r')
+ {
+ skipNextLf = true;
+ }
+ else
+ {
+ skipNextLf = false;
+ }
+
+ if (!line.empty())
+ {
+ EnqueueLine(line.c_str());
+ line.clear();
+ }
+
+ printf("server> ");
+ fflush(stdout);
+ continue;
+ }
+
+ skipNextLf = false;
+
+ if ((unsigned char)ch == 3)
+ {
+ continue;
+ }
+
+ if ((unsigned char)ch == 8 || (unsigned char)ch == 127)
+ {
+ if (!line.empty())
+ {
+ line.resize(line.size() - 1);
+ }
+ continue;
+ }
+
+ if (isprint((unsigned char)ch) && line.size() < 4096)
+ {
+ line.push_back(ch);
+ }
+ }
+ }
+
+ void ServerCliInput::EnqueueLine(const char *line)
+ {
+ if (line == NULL || line[0] == 0 || m_engine == NULL)
+ {
+ return;
+ }
+
+ // Keep local history and forward command for main-thread execution.
+ linenoiseHistoryAdd(line);
+ m_engine->EnqueueCommandLine(line);
+ }
+
+ void ServerCliInput::CompletionThunk(const char *line, linenoiseCompletions *completions)
+ {
+ // Static thunk forwards callback into instance state.
+ if (s_instance != NULL)
+ {
+ s_instance->BuildCompletions(line, completions);
+ }
+ }
+
+ void ServerCliInput::BuildCompletions(const char *line, linenoiseCompletions *completions)
+ {
+ if (line == NULL || completions == NULL || m_engine == NULL)
+ {
+ return;
+ }
+
+ std::vector<std::string> suggestions;
+ m_engine->BuildCompletions(line, &suggestions);
+ for (size_t i = 0; i < suggestions.size(); ++i)
+ {
+ linenoiseAddCompletion(completions, suggestions[i].c_str());
+ }
+ }
+}
diff --git a/Minecraft.Server/Console/ServerCliInput.h b/Minecraft.Server/Console/ServerCliInput.h
new file mode 100644
index 00000000..83221a2c
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCliInput.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <atomic>
+#include <thread>
+
+struct linenoiseCompletions;
+
+namespace ServerRuntime
+{
+ class ServerCliEngine;
+
+ /**
+ * **CLI input worker**
+ *
+ * Owns the interactive input thread and bridges linenoise callbacks to the engine.
+ * 入力スレッドと補完コールバックを管理するクラス
+ */
+ class ServerCliInput
+ {
+ public:
+ ServerCliInput();
+ ~ServerCliInput();
+
+ /**
+ * **Start input loop**
+ *
+ * Binds to an engine and starts reading user input from the console.
+ * エンジンに接続して入力ループを開始
+ */
+ void Start(ServerCliEngine *engine);
+
+ /**
+ * **Stop input loop**
+ *
+ * Requests stop and joins the input thread.
+ * 停止要求を出して入力スレッドを終了
+ */
+ void Stop();
+
+ /**
+ * **Check running state**
+ *
+ * Returns true while the input thread is active.
+ * 入力処理が動作中かどうか
+ */
+ bool IsRunning() const;
+
+ private:
+ void RunInputLoop();
+ void RunLinenoiseLoop();
+ void RunStreamInputLoop();
+ void EnqueueLine(const char *line);
+ static void CompletionThunk(const char *line, linenoiseCompletions *completions);
+ void BuildCompletions(const char *line, linenoiseCompletions *completions);
+
+ private:
+ std::atomic<bool> m_running;
+ std::thread m_inputThread;
+ ServerCliEngine *m_engine;
+
+ static ServerCliInput *s_instance;
+ };
+}
diff --git a/Minecraft.Server/Console/ServerCliParser.cpp b/Minecraft.Server/Console/ServerCliParser.cpp
new file mode 100644
index 00000000..5888153f
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCliParser.cpp
@@ -0,0 +1,116 @@
+#include "stdafx.h"
+
+#include "ServerCliParser.h"
+
+namespace ServerRuntime
+{
+ static void TokenizeLine(const std::string &line, std::vector<std::string> *tokens, bool *trailingSpace)
+ {
+ std::string current;
+ bool inQuotes = false;
+ bool escaped = false;
+
+ tokens->clear();
+ *trailingSpace = false;
+
+ for (size_t i = 0; i < line.size(); ++i)
+ {
+ char ch = line[i];
+ if (escaped)
+ {
+ // Keep escaped character literally (e.g. \" or \ ).
+ current.push_back(ch);
+ escaped = false;
+ continue;
+ }
+
+ if (ch == '\\')
+ {
+ escaped = true;
+ continue;
+ }
+
+ if (ch == '"')
+ {
+ // Double quotes group spaces into one token.
+ inQuotes = !inQuotes;
+ continue;
+ }
+
+ if (!inQuotes && (ch == ' ' || ch == '\t'))
+ {
+ if (!current.empty())
+ {
+ tokens->push_back(current);
+ current.clear();
+ }
+ continue;
+ }
+
+ current.push_back(ch);
+ }
+
+ if (!current.empty())
+ {
+ tokens->push_back(current);
+ }
+
+ if (!line.empty())
+ {
+ char tail = line[line.size() - 1];
+ // Trailing space means completion targets the next token slot.
+ *trailingSpace = (!inQuotes && (tail == ' ' || tail == '\t'));
+ }
+ }
+
+ ServerCliParsedLine ServerCliParser::Parse(const std::string &line)
+ {
+ ServerCliParsedLine parsed;
+ parsed.raw = line;
+ TokenizeLine(line, &parsed.tokens, &parsed.trailingSpace);
+ return parsed;
+ }
+
+ ServerCliCompletionContext ServerCliParser::BuildCompletionContext(const std::string &line)
+ {
+ ServerCliCompletionContext context;
+ context.parsed = Parse(line);
+
+ if (context.parsed.tokens.empty())
+ {
+ context.currentTokenIndex = 0;
+ context.prefix.clear();
+ context.linePrefix.clear();
+ return context;
+ }
+
+ if (context.parsed.trailingSpace)
+ {
+ // Cursor is after a separator, so complete a new token.
+ context.currentTokenIndex = context.parsed.tokens.size();
+ context.prefix.clear();
+ }
+ else
+ {
+ // Cursor is inside current token, so complete by its prefix.
+ context.currentTokenIndex = context.parsed.tokens.size() - 1;
+ context.prefix = context.parsed.tokens.back();
+ }
+
+ for (size_t i = 0; i < context.currentTokenIndex; ++i)
+ {
+ // linePrefix is the immutable left side reused by completion output.
+ if (!context.linePrefix.empty())
+ {
+ context.linePrefix.push_back(' ');
+ }
+ context.linePrefix += context.parsed.tokens[i];
+ }
+ if (!context.linePrefix.empty())
+ {
+ context.linePrefix.push_back(' ');
+ }
+
+ return context;
+ }
+}
diff --git a/Minecraft.Server/Console/ServerCliParser.h b/Minecraft.Server/Console/ServerCliParser.h
new file mode 100644
index 00000000..a84d179b
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCliParser.h
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace ServerRuntime
+{
+ /**
+ * **Parsed command line**
+ */
+ struct ServerCliParsedLine
+ {
+ std::string raw;
+ std::vector<std::string> tokens;
+ bool trailingSpace;
+
+ ServerCliParsedLine()
+ : trailingSpace(false)
+ {
+ }
+ };
+
+ /**
+ * **Completion context for one input line**
+ *
+ * Indicates current token index, token prefix, and the fixed line prefix.
+ */
+ struct ServerCliCompletionContext
+ {
+ ServerCliParsedLine parsed;
+ size_t currentTokenIndex;
+ std::string prefix;
+ std::string linePrefix;
+
+ ServerCliCompletionContext()
+ : currentTokenIndex(0)
+ {
+ }
+ };
+
+ /**
+ * **CLI parser helpers**
+ *
+ * Converts raw input text into tokenized data used by execution and completion.
+ */
+ class ServerCliParser
+ {
+ public:
+ /**
+ * **Tokenize one command line**
+ *
+ * Supports quoted segments and escaped characters.
+ */
+ static ServerCliParsedLine Parse(const std::string &line);
+
+ /**
+ * **Build completion metadata**
+ *
+ * Determines active token position and reusable prefix parts.
+ */
+ static ServerCliCompletionContext BuildCompletionContext(const std::string &line);
+ };
+}
diff --git a/Minecraft.Server/Console/ServerCliRegistry.cpp b/Minecraft.Server/Console/ServerCliRegistry.cpp
new file mode 100644
index 00000000..432907b2
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCliRegistry.cpp
@@ -0,0 +1,99 @@
+#include "stdafx.h"
+
+#include "ServerCliRegistry.h"
+
+#include "commands\IServerCliCommand.h"
+#include "..\Common\StringUtils.h"
+
+namespace ServerRuntime
+{
+ bool ServerCliRegistry::Register(std::unique_ptr<IServerCliCommand> command)
+ {
+ if (!command)
+ {
+ return false;
+ }
+
+ IServerCliCommand *raw = command.get();
+ std::string baseName = StringUtils::ToLowerAscii(raw->Name());
+ // Reject empty/duplicate primary command names.
+ if (baseName.empty() || m_lookup.find(baseName) != m_lookup.end())
+ {
+ return false;
+ }
+ std::vector<std::string> aliases = raw->Aliases();
+ std::vector<std::string> normalizedAliases;
+ normalizedAliases.reserve(aliases.size());
+ for (size_t i = 0; i < aliases.size(); ++i)
+ {
+ std::string alias = StringUtils::ToLowerAscii(aliases[i]);
+ // Alias must also be unique across all names and aliases.
+ if (alias.empty() || m_lookup.find(alias) != m_lookup.end())
+ {
+ return false;
+ }
+ normalizedAliases.push_back(alias);
+ }
+
+ m_lookup[baseName] = raw;
+ for (size_t i = 0; i < normalizedAliases.size(); ++i)
+ {
+ m_lookup[normalizedAliases[i]] = raw;
+ }
+
+ // Command objects are owned here; lookup stores non-owning pointers.
+ m_commands.push_back(std::move(command));
+ return true;
+ }
+
+ const IServerCliCommand *ServerCliRegistry::Find(const std::string &name) const
+ {
+ std::string key = StringUtils::ToLowerAscii(name);
+ auto it = m_lookup.find(key);
+ if (it == m_lookup.end())
+ {
+ return NULL;
+ }
+ return it->second;
+ }
+
+ IServerCliCommand *ServerCliRegistry::FindMutable(const std::string &name)
+ {
+ std::string key = StringUtils::ToLowerAscii(name);
+ auto it = m_lookup.find(key);
+ if (it == m_lookup.end())
+ {
+ return NULL;
+ }
+ return it->second;
+ }
+
+ void ServerCliRegistry::SuggestCommandNames(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const
+ {
+ for (size_t i = 0; i < m_commands.size(); ++i)
+ {
+ const IServerCliCommand *command = m_commands[i].get();
+ std::string name = command->Name();
+ if (StringUtils::StartsWithIgnoreCase(name, prefix))
+ {
+ out->push_back(linePrefix + name);
+ }
+
+ // Include aliases so users can discover shorthand commands.
+ std::vector<std::string> aliases = command->Aliases();
+ for (size_t aliasIndex = 0; aliasIndex < aliases.size(); ++aliasIndex)
+ {
+ if (StringUtils::StartsWithIgnoreCase(aliases[aliasIndex], prefix))
+ {
+ out->push_back(linePrefix + aliases[aliasIndex]);
+ }
+ }
+ }
+ }
+
+ const std::vector<std::unique_ptr<IServerCliCommand>> &ServerCliRegistry::Commands() const
+ {
+ return m_commands;
+ }
+}
+
diff --git a/Minecraft.Server/Console/ServerCliRegistry.h b/Minecraft.Server/Console/ServerCliRegistry.h
new file mode 100644
index 00000000..5104b534
--- /dev/null
+++ b/Minecraft.Server/Console/ServerCliRegistry.h
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace ServerRuntime
+{
+ class IServerCliCommand;
+
+ /**
+ * **CLI command registry**
+ */
+ class ServerCliRegistry
+ {
+ public:
+ /**
+ * **Register a command object**
+ *
+ * Validates name/aliases and adds lookup entries.
+ * コマンドの追加
+ */
+ bool Register(std::unique_ptr<IServerCliCommand> command);
+
+ /**
+ * **Find command by name or alias (const)**
+ *
+ * Returns null when no match exists.
+ */
+ const IServerCliCommand *Find(const std::string &name) const;
+
+ /**
+ * **Find mutable command by name or alias**
+ *
+ * Used by runtime dispatch path.
+ */
+ IServerCliCommand *FindMutable(const std::string &name);
+
+ /**
+ * **Suggest top-level command names**
+ *
+ * Adds matching command names and aliases to the output list.
+ */
+ void SuggestCommandNames(const std::string &prefix, const std::string &linePrefix, std::vector<std::string> *out) const;
+
+ /**
+ * **Get registered command list**
+ *
+ * Intended for help output and inspection.
+ */
+ const std::vector<std::unique_ptr<IServerCliCommand>> &Commands() const;
+
+ private:
+ std::vector<std::unique_ptr<IServerCliCommand>> m_commands;
+ std::unordered_map<std::string, IServerCliCommand *> m_lookup;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/CommandParsing.h b/Minecraft.Server/Console/commands/CommandParsing.h
new file mode 100644
index 00000000..edef68d0
--- /dev/null
+++ b/Minecraft.Server/Console/commands/CommandParsing.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <cerrno>
+#include <cstdlib>
+#include <limits>
+#include <string>
+
+namespace ServerRuntime
+{
+ namespace CommandParsing
+ {
+ inline bool TryParseInt(const std::string &text, int *outValue)
+ {
+ if (outValue == nullptr || text.empty())
+ {
+ return false;
+ }
+
+ char *end = nullptr;
+ errno = 0;
+ const long parsedValue = std::strtol(text.c_str(), &end, 10);
+ if (end == text.c_str() || *end != '\0')
+ {
+ return false;
+ }
+ if (errno == ERANGE)
+ {
+ return false;
+ }
+ if (parsedValue < (std::numeric_limits<int>::min)() || parsedValue > (std::numeric_limits<int>::max)())
+ {
+ return false;
+ }
+
+ *outValue = static_cast<int>(parsedValue);
+ return true;
+ }
+ }
+}
diff --git a/Minecraft.Server/Console/commands/IServerCliCommand.h b/Minecraft.Server/Console/commands/IServerCliCommand.h
new file mode 100644
index 00000000..9cf5ef0e
--- /dev/null
+++ b/Minecraft.Server/Console/commands/IServerCliCommand.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace ServerRuntime
+{
+ class ServerCliEngine;
+ struct ServerCliParsedLine;
+ struct ServerCliCompletionContext;
+
+ /**
+ * **Command interface for server CLI**
+ *
+ * Implement this contract to add new commands without changing engine internals.
+ */
+ class IServerCliCommand
+ {
+ public:
+ virtual ~IServerCliCommand() = default;
+
+ /** Primary command name */
+ virtual const char *Name() const = 0;
+ /** Optional aliases */
+ virtual std::vector<std::string> Aliases() const { return {}; }
+ /** Usage text for help */
+ virtual const char *Usage() const = 0;
+ /** Short command description*/
+ virtual const char *Description() const = 0;
+
+ /**
+ * **Execute command logic**
+ *
+ * Called after tokenization and command lookup.
+ */
+ virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) = 0;
+
+ /**
+ * **Provide argument completion candidates**
+ *
+ * Override when command-specific completion is needed.
+ */
+ virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ (void)context;
+ (void)engine;
+ (void)out;
+ }
+ };
+}
diff --git a/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp b/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp
new file mode 100644
index 00000000..99c1455e
--- /dev/null
+++ b/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp
@@ -0,0 +1,171 @@
+#include "stdafx.h"
+
+#include "CliCommandBanIp.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\Access\Access.h"
+#include "..\..\..\Common\NetworkUtils.h"
+#include "..\..\..\Common\StringUtils.h"
+#include "..\..\..\ServerLogManager.h"
+#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
+#include "..\..\..\..\Minecraft.Client\PlayerConnection.h"
+#include "..\..\..\..\Minecraft.Client\PlayerList.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+#include "..\..\..\..\Minecraft.World\Connection.h"
+#include "..\..\..\..\Minecraft.World\DisconnectPacket.h"
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ // The dedicated server keeps the accepted remote IP in ServerLogManager, keyed by connection smallId.
+ // It's a bit strange from a responsibility standpoint, so we'll need to implement it separately.
+ static bool TryGetPlayerRemoteIp(const std::shared_ptr<ServerPlayer> &player, std::string *outIp)
+ {
+ if (outIp == nullptr || player == nullptr || player->connection == nullptr || player->connection->connection == nullptr || player->connection->connection->getSocket() == nullptr)
+ {
+ return false;
+ }
+
+ const unsigned char smallId = player->connection->connection->getSocket()->getSmallId();
+ if (smallId == 0)
+ {
+ return false;
+ }
+
+ return ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, outIp);
+ }
+
+ // After persisting the ban, walk a snapshot of current players so every matching session is removed.
+ static int DisconnectPlayersByRemoteIp(const std::string &ip)
+ {
+ auto *server = MinecraftServer::getInstance();
+ if (server == nullptr || server->getPlayers() == nullptr)
+ {
+ return 0;
+ }
+
+ const std::string normalizedIp = NetworkUtils::NormalizeIpToken(ip);
+ const std::vector<std::shared_ptr<ServerPlayer>> playerSnapshot = server->getPlayers()->players;
+ int disconnectedCount = 0;
+ for (const auto &player : playerSnapshot)
+ {
+ std::string playerIp;
+ if (!TryGetPlayerRemoteIp(player, &playerIp))
+ {
+ continue;
+ }
+
+ if (NetworkUtils::NormalizeIpToken(playerIp) == normalizedIp)
+ {
+ if (player != nullptr && player->connection != nullptr)
+ {
+ player->connection->disconnect(DisconnectPacket::eDisconnect_Banned);
+ ++disconnectedCount;
+ }
+ }
+ }
+
+ return disconnectedCount;
+ }
+ }
+
+ const char *CliCommandBanIp::Name() const
+ {
+ return "ban-ip";
+ }
+
+ const char *CliCommandBanIp::Usage() const
+ {
+ return "ban-ip <address|player> [reason ...]";
+ }
+
+ const char *CliCommandBanIp::Description() const
+ {
+ return "Ban an IP address or a player's current IP.";
+ }
+
+ /**
+ * Resolves either a literal IP or an online player's current IP, persists the ban, and disconnects every matching connection
+ * IPまたは接続中プレイヤーの現在IPをBANし一致する接続を切断する
+ */
+ bool CliCommandBanIp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() < 2)
+ {
+ engine->LogWarn("Usage: ban-ip <address|player> [reason ...]");
+ return false;
+ }
+ if (!ServerRuntime::Access::IsInitialized())
+ {
+ engine->LogWarn("Access manager is not initialized.");
+ return false;
+ }
+
+ const std::string targetToken = line.tokens[1];
+ std::string remoteIp;
+ // Match Java Edition behavior by accepting either a literal IP or an online player name.
+ const auto targetPlayer = engine->FindPlayerByNameUtf8(targetToken);
+ if (targetPlayer != nullptr)
+ {
+ if (!TryGetPlayerRemoteIp(targetPlayer, &remoteIp))
+ {
+ engine->LogWarn("Cannot ban that player's IP because no current remote IP is available.");
+ return false;
+ }
+ }
+ else if (NetworkUtils::IsIpLiteral(targetToken))
+ {
+ remoteIp = StringUtils::TrimAscii(targetToken);
+ }
+ else
+ {
+ engine->LogWarn("Unknown player or invalid IP address: " + targetToken);
+ return false;
+ }
+
+ // Refuse duplicate bans so operators get immediate feedback instead of rewriting the same entry.
+ if (ServerRuntime::Access::IsIpBanned(remoteIp))
+ {
+ engine->LogWarn("That IP address is already banned.");
+ return false;
+ }
+
+ ServerRuntime::Access::BanMetadata metadata = ServerRuntime::Access::BanManager::BuildDefaultMetadata("Console");
+ metadata.reason = StringUtils::JoinTokens(line.tokens, 2);
+ if (metadata.reason.empty())
+ {
+ metadata.reason = "Banned by an operator.";
+ }
+
+ // Publish the ban before disconnecting players so reconnect attempts are rejected immediately.
+ if (!ServerRuntime::Access::AddIpBan(remoteIp, metadata))
+ {
+ engine->LogError("Failed to write IP ban.");
+ return false;
+ }
+
+ const int disconnectedCount = DisconnectPlayersByRemoteIp(remoteIp);
+ // Report the resolved IP rather than the original token so player-name targets are explicit in the console.
+ engine->LogInfo("Banned IP address " + remoteIp + ".");
+ if (disconnectedCount > 0)
+ {
+ engine->LogInfo("Disconnected " + std::to_string(disconnectedCount) + " player(s) with that IP.");
+ }
+ return true;
+ }
+
+ /**
+ * Suggests online player names for the player-target form of the Java Edition command
+ * プレイヤー名指定時の補完候補を返す
+ */
+ void CliCommandBanIp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex == 1)
+ {
+ engine->SuggestPlayers(context.prefix, context.linePrefix, out);
+ }
+ }
+}
+
diff --git a/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h b/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h
new file mode 100644
index 00000000..1c116fa6
--- /dev/null
+++ b/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ /**
+ * Applies a dedicated-server IP ban using Java Edition style syntax and Access-backed persistence
+ */
+ class CliCommandBanIp : public IServerCliCommand
+ {
+ public:
+ virtual const char *Name() const;
+ virtual const char *Usage() const;
+ virtual const char *Description() const;
+ virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
+ virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp b/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp
new file mode 100644
index 00000000..14641617
--- /dev/null
+++ b/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp
@@ -0,0 +1,136 @@
+#include "stdafx.h"
+
+#include "CliCommandBanList.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\Access\Access.h"
+#include "..\..\..\Common\StringUtils.h"
+
+#include <algorithm>
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ static void AppendUniqueText(const std::string &text, std::vector<std::string> *out)
+ {
+ if (out == nullptr || text.empty())
+ {
+ return;
+ }
+
+ if (std::find(out->begin(), out->end(), text) == out->end())
+ {
+ out->push_back(text);
+ }
+ }
+
+ static bool CompareLowerAscii(const std::string &left, const std::string &right)
+ {
+ return StringUtils::ToLowerAscii(left) < StringUtils::ToLowerAscii(right);
+ }
+
+ static bool LogBannedPlayers(ServerCliEngine *engine)
+ {
+ std::vector<ServerRuntime::Access::BannedPlayerEntry> entries;
+ if (!ServerRuntime::Access::SnapshotBannedPlayers(&entries))
+ {
+ engine->LogError("Failed to read banned players.");
+ return false;
+ }
+
+ std::vector<std::string> names;
+ for (const auto &entry : entries)
+ {
+ AppendUniqueText(entry.name, &names);
+ }
+ std::sort(names.begin(), names.end(), CompareLowerAscii);
+
+ engine->LogInfo("There are " + std::to_string(names.size()) + " banned player(s).");
+ for (const auto &name : names)
+ {
+ engine->LogInfo(" " + name);
+ }
+ return true;
+ }
+
+ static bool LogBannedIps(ServerCliEngine *engine)
+ {
+ std::vector<ServerRuntime::Access::BannedIpEntry> entries;
+ if (!ServerRuntime::Access::SnapshotBannedIps(&entries))
+ {
+ engine->LogError("Failed to read banned IPs.");
+ return false;
+ }
+
+ std::vector<std::string> ips;
+ for (const auto &entry : entries)
+ {
+ AppendUniqueText(entry.ip, &ips);
+ }
+ std::sort(ips.begin(), ips.end(), CompareLowerAscii);
+
+ engine->LogInfo("There are " + std::to_string(ips.size()) + " banned IP(s).");
+ for (const auto &ip : ips)
+ {
+ engine->LogInfo(" " + ip);
+ }
+ return true;
+ }
+
+ static bool LogAllBans(ServerCliEngine *engine)
+ {
+ if (!LogBannedPlayers(engine))
+ {
+ return false;
+ }
+
+ // Always print the IP snapshot as well so ban-ip entries are visible from the same command output.
+ return LogBannedIps(engine);
+ }
+ }
+
+ const char *CliCommandBanList::Name() const
+ {
+ return "banlist";
+ }
+
+ const char *CliCommandBanList::Usage() const
+ {
+ return "banlist";
+ }
+
+ const char *CliCommandBanList::Description() const
+ {
+ return "List all banned players and IPs.";
+ }
+
+ /**
+ * Reads the current Access snapshots and always prints both banned players and banned IPs
+ * Access の一覧を読みプレイヤーBANとIP BANをまとめて表示する
+ */
+ bool CliCommandBanList::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() > 1)
+ {
+ engine->LogWarn("Usage: banlist");
+ return false;
+ }
+ if (!ServerRuntime::Access::IsInitialized())
+ {
+ engine->LogWarn("Access manager is not initialized.");
+ return false;
+ }
+
+ return LogAllBans(engine);
+ }
+
+ void CliCommandBanList::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ (void)context;
+ (void)engine;
+ (void)out;
+ }
+}
+
diff --git a/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.h b/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.h
new file mode 100644
index 00000000..1db32bc1
--- /dev/null
+++ b/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ /**
+ * **Ban List Command**
+ *
+ * Lists dedicated-server player bans and IP bans in a single command output
+ * 専用サーバーのプレイヤーBANとIP BANをまとめて表示する
+ */
+ class CliCommandBanList : public IServerCliCommand
+ {
+ public:
+ virtual const char *Name() const;
+ virtual const char *Usage() const;
+ virtual const char *Description() const;
+ virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
+ virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
+ };
+}
+
diff --git a/Minecraft.Server/Console/commands/ban/CliCommandBan.cpp b/Minecraft.Server/Console/commands/ban/CliCommandBan.cpp
new file mode 100644
index 00000000..f9855c0c
--- /dev/null
+++ b/Minecraft.Server/Console/commands/ban/CliCommandBan.cpp
@@ -0,0 +1,145 @@
+#include "stdafx.h"
+
+#include "CliCommandBan.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\Access\Access.h"
+#include "..\..\..\Common\StringUtils.h"
+#include "..\..\..\..\Minecraft.Client\PlayerConnection.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+#include "..\..\..\..\Minecraft.World\DisconnectPacket.h"
+
+#include <algorithm>
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ static void AppendUniqueXuid(PlayerUID xuid, std::vector<PlayerUID> *out)
+ {
+ if (out == nullptr || xuid == INVALID_XUID)
+ {
+ return;
+ }
+
+ if (std::find(out->begin(), out->end(), xuid) == out->end())
+ {
+ out->push_back(xuid);
+ }
+ }
+
+ static void CollectPlayerBanXuids(const std::shared_ptr<ServerPlayer> &player, std::vector<PlayerUID> *out)
+ {
+ if (player == nullptr || out == nullptr)
+ {
+ return;
+ }
+
+ // Keep both identity variants because the dedicated server checks login and online XUIDs separately.
+ AppendUniqueXuid(player->getXuid(), out);
+ AppendUniqueXuid(player->getOnlineXuid(), out);
+ }
+ }
+
+ const char *CliCommandBan::Name() const
+ {
+ return "ban";
+ }
+
+ const char *CliCommandBan::Usage() const
+ {
+ return "ban <player> [reason ...]";
+ }
+
+ const char *CliCommandBan::Description() const
+ {
+ return "Ban an online player.";
+ }
+
+ /**
+ * Resolves the live player, writes one or more Access ban entries, and disconnects the target with the banned reason
+ * 対象プレイヤーを解決してBANを保存し切断する
+ */
+ bool CliCommandBan::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() < 2)
+ {
+ engine->LogWarn("Usage: ban <player> [reason ...]");
+ return false;
+ }
+ if (!ServerRuntime::Access::IsInitialized())
+ {
+ engine->LogWarn("Access manager is not initialized.");
+ return false;
+ }
+
+ const auto target = engine->FindPlayerByNameUtf8(line.tokens[1]);
+ if (target == nullptr)
+ {
+ engine->LogWarn("Unknown player: " + line.tokens[1] + " (this server build can only ban players that are currently online).");
+ return false;
+ }
+
+ std::vector<PlayerUID> xuids;
+ CollectPlayerBanXuids(target, &xuids);
+ if (xuids.empty())
+ {
+ engine->LogWarn("Cannot ban that player because no valid XUID is available.");
+ return false;
+ }
+
+ const bool hasUnbannedIdentity = std::any_of(
+ xuids.begin(),
+ xuids.end(),
+ [](PlayerUID xuid) { return !ServerRuntime::Access::IsPlayerBanned(xuid); });
+ if (!hasUnbannedIdentity)
+ {
+ engine->LogWarn("That player is already banned.");
+ return false;
+ }
+
+ ServerRuntime::Access::BanMetadata metadata = ServerRuntime::Access::BanManager::BuildDefaultMetadata("Console");
+ metadata.reason = StringUtils::JoinTokens(line.tokens, 2);
+ if (metadata.reason.empty())
+ {
+ metadata.reason = "Banned by an operator.";
+ }
+
+ const std::string playerName = StringUtils::WideToUtf8(target->getName());
+ for (const auto xuid : xuids)
+ {
+ if (ServerRuntime::Access::IsPlayerBanned(xuid))
+ {
+ continue;
+ }
+
+ if (!ServerRuntime::Access::AddPlayerBan(xuid, playerName, metadata))
+ {
+ engine->LogError("Failed to write player ban.");
+ return false;
+ }
+ }
+
+ if (target->connection != nullptr)
+ {
+ target->connection->disconnect(DisconnectPacket::eDisconnect_Banned);
+ }
+
+ engine->LogInfo("Banned player " + playerName + ".");
+ return true;
+ }
+
+ /**
+ * Suggests currently connected player names for the Java-style player argument
+ * プレイヤー引数の補完候補を返す
+ */
+ void CliCommandBan::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex == 1)
+ {
+ engine->SuggestPlayers(context.prefix, context.linePrefix, out);
+ }
+ }
+}
+
diff --git a/Minecraft.Server/Console/commands/ban/CliCommandBan.h b/Minecraft.Server/Console/commands/ban/CliCommandBan.h
new file mode 100644
index 00000000..8605474c
--- /dev/null
+++ b/Minecraft.Server/Console/commands/ban/CliCommandBan.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ /**
+ * Applies a dedicated-server player ban using Java Edition style syntax and Access-backed persistence
+ * Java Edition 風の ban コマンドで永続プレイヤーBANを行う
+ */
+ class CliCommandBan : public IServerCliCommand
+ {
+ public:
+ virtual const char *Name() const;
+ virtual const char *Usage() const;
+ virtual const char *Description() const;
+ virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
+ virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp b/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp
new file mode 100644
index 00000000..ee0e35a2
--- /dev/null
+++ b/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp
@@ -0,0 +1,117 @@
+#include "stdafx.h"
+
+#include "CliCommandDefaultGamemode.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
+#include "..\..\..\..\Minecraft.Client\PlayerList.h"
+#include "..\..\..\..\Minecraft.Client\ServerLevel.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+#include "..\..\..\..\Minecraft.World\net.minecraft.world.level.storage.h"
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ constexpr const char *kDefaultGamemodeUsage = "defaultgamemode <survival|creative|0|1>";
+
+ static std::string ModeLabel(GameType *mode)
+ {
+ if (mode == GameType::SURVIVAL)
+ {
+ return "survival";
+ }
+ if (mode == GameType::CREATIVE)
+ {
+ return "creative";
+ }
+ if (mode == GameType::ADVENTURE)
+ {
+ return "adventure";
+ }
+
+ return std::to_string(mode != nullptr ? mode->getId() : -1);
+ }
+ }
+
+ const char *CliCommandDefaultGamemode::Name() const
+ {
+ return "defaultgamemode";
+ }
+
+ const char *CliCommandDefaultGamemode::Usage() const
+ {
+ return kDefaultGamemodeUsage;
+ }
+
+ const char *CliCommandDefaultGamemode::Description() const
+ {
+ return "Set the default game mode (server-side implementation).";
+ }
+
+ bool CliCommandDefaultGamemode::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() != 2)
+ {
+ engine->LogWarn(std::string("Usage: ") + kDefaultGamemodeUsage);
+ return false;
+ }
+
+ GameType *mode = engine->ParseGamemode(line.tokens[1]);
+ if (mode == nullptr)
+ {
+ engine->LogWarn("Unknown gamemode: " + line.tokens[1]);
+ return false;
+ }
+
+ MinecraftServer *server = MinecraftServer::getInstance();
+ if (server == nullptr)
+ {
+ engine->LogWarn("MinecraftServer instance is not available.");
+ return false;
+ }
+
+ PlayerList *players = server->getPlayers();
+ if (players == nullptr)
+ {
+ engine->LogWarn("Player list is not available.");
+ return false;
+ }
+
+ players->setOverrideGameMode(mode);
+
+ for (unsigned int i = 0; i < server->levels.length; ++i)
+ {
+ ServerLevel *level = server->levels[i];
+ if (level != nullptr && level->getLevelData() != nullptr)
+ {
+ level->getLevelData()->setGameType(mode);
+ }
+ }
+
+ if (server->getForceGameType())
+ {
+ for (size_t i = 0; i < players->players.size(); ++i)
+ {
+ std::shared_ptr<ServerPlayer> player = players->players[i];
+ if (player != nullptr)
+ {
+ player->setGameMode(mode);
+ player->fallDistance = 0.0f;
+ }
+ }
+ }
+
+ engine->LogInfo("Default gamemode set to " + ModeLabel(mode) + ".");
+ return true;
+ }
+
+ void CliCommandDefaultGamemode::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex == 1)
+ {
+ engine->SuggestGamemodes(context.prefix, context.linePrefix, out);
+ }
+ }
+}
diff --git a/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.h b/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.h
new file mode 100644
index 00000000..5cc17b34
--- /dev/null
+++ b/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandDefaultGamemode : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp b/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp
new file mode 100644
index 00000000..70d4d7d6
--- /dev/null
+++ b/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp
@@ -0,0 +1,87 @@
+#include "stdafx.h"
+
+#include "CliCommandEnchant.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\CommandParsing.h"
+#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
+#include "..\..\..\..\Minecraft.World\EnchantItemCommand.h"
+#include "..\..\..\..\Minecraft.World\net.minecraft.world.entity.player.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ constexpr const char *kEnchantUsage = "enchant <player> <enchantId> [level]";
+ }
+
+ const char *CliCommandEnchant::Name() const
+ {
+ return "enchant";
+ }
+
+ const char *CliCommandEnchant::Usage() const
+ {
+ return kEnchantUsage;
+ }
+
+ const char *CliCommandEnchant::Description() const
+ {
+ return "Enchant held item via Minecraft.World command dispatcher.";
+ }
+
+ bool CliCommandEnchant::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() < 3 || line.tokens.size() > 4)
+ {
+ engine->LogWarn(std::string("Usage: ") + kEnchantUsage);
+ return false;
+ }
+
+ std::shared_ptr<ServerPlayer> target = engine->FindPlayerByNameUtf8(line.tokens[1]);
+ if (target == nullptr)
+ {
+ engine->LogWarn("Unknown player: " + line.tokens[1]);
+ return false;
+ }
+
+ int enchantmentId = 0;
+ int enchantmentLevel = 1;
+ if (!CommandParsing::TryParseInt(line.tokens[2], &enchantmentId))
+ {
+ engine->LogWarn("Invalid enchantment id: " + line.tokens[2]);
+ return false;
+ }
+ if (line.tokens.size() >= 4 && !CommandParsing::TryParseInt(line.tokens[3], &enchantmentLevel))
+ {
+ engine->LogWarn("Invalid enchantment level: " + line.tokens[3]);
+ return false;
+ }
+
+ std::shared_ptr<Player> player = std::dynamic_pointer_cast<Player>(target);
+ if (player == nullptr)
+ {
+ engine->LogWarn("Cannot resolve target player entity.");
+ return false;
+ }
+
+ std::shared_ptr<GameCommandPacket> packet = EnchantItemCommand::preparePacket(player, enchantmentId, enchantmentLevel);
+ if (packet == nullptr)
+ {
+ engine->LogError("Failed to build enchant command packet.");
+ return false;
+ }
+
+ return engine->DispatchWorldCommand(packet->command, packet->data);
+ }
+
+ void CliCommandEnchant::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex == 1)
+ {
+ engine->SuggestPlayers(context.prefix, context.linePrefix, out);
+ }
+ }
+}
diff --git a/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.h b/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.h
new file mode 100644
index 00000000..66e330bd
--- /dev/null
+++ b/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandEnchant : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp b/Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp
new file mode 100644
index 00000000..77df99ae
--- /dev/null
+++ b/Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp
@@ -0,0 +1,184 @@
+#include "stdafx.h"
+
+#include "CliCommandExperience.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\CommandParsing.h"
+#include "..\..\..\Common\StringUtils.h"
+#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
+#include "..\..\..\..\Minecraft.Client\PlayerList.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+
+#include <limits>
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ constexpr const char *kExperienceUsage = "xp <amount>[L] [player]";
+ constexpr const char *kExperienceUsageWithPlayer = "xp <amount>[L] <player>";
+
+ struct ExperienceAmount
+ {
+ int amount = 0;
+ bool levels = false;
+ bool take = false;
+ };
+
+ static bool TryParseExperienceAmount(const std::string &token, ExperienceAmount *outValue)
+ {
+ if (outValue == nullptr || token.empty())
+ {
+ return false;
+ }
+
+ ExperienceAmount parsed;
+ std::string numericToken = token;
+ const char suffix = token[token.size() - 1];
+ if (suffix == 'l' || suffix == 'L')
+ {
+ parsed.levels = true;
+ numericToken = token.substr(0, token.size() - 1);
+ if (numericToken.empty())
+ {
+ return false;
+ }
+ }
+
+ int signedAmount = 0;
+ if (!CommandParsing::TryParseInt(numericToken, &signedAmount))
+ {
+ return false;
+ }
+ if (signedAmount == (std::numeric_limits<int>::min)())
+ {
+ return false;
+ }
+
+ parsed.take = signedAmount < 0;
+ parsed.amount = parsed.take ? -signedAmount : signedAmount;
+ *outValue = parsed;
+ return true;
+ }
+
+ static std::shared_ptr<ServerPlayer> ResolveTargetPlayer(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() >= 3)
+ {
+ return engine->FindPlayerByNameUtf8(line.tokens[2]);
+ }
+
+ MinecraftServer *server = MinecraftServer::getInstance();
+ if (server == nullptr || server->getPlayers() == nullptr)
+ {
+ return nullptr;
+ }
+
+ PlayerList *players = server->getPlayers();
+ if (players->players.size() == 1 && players->players[0] != nullptr)
+ {
+ return players->players[0];
+ }
+
+ return nullptr;
+ }
+ }
+
+ const char *CliCommandExperience::Name() const
+ {
+ return "xp";
+ }
+
+ std::vector<std::string> CliCommandExperience::Aliases() const
+ {
+ return { "experience" };
+ }
+
+ const char *CliCommandExperience::Usage() const
+ {
+ return kExperienceUsage;
+ }
+
+ const char *CliCommandExperience::Description() const
+ {
+ return "Grant or remove experience (server-side implementation).";
+ }
+
+ bool CliCommandExperience::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() < 2 || line.tokens.size() > 3)
+ {
+ engine->LogWarn(std::string("Usage: ") + kExperienceUsage);
+ return false;
+ }
+
+ ExperienceAmount amount;
+ if (!TryParseExperienceAmount(line.tokens[1], &amount))
+ {
+ engine->LogWarn(std::string("Usage: ") + kExperienceUsage);
+ return false;
+ }
+
+ std::shared_ptr<ServerPlayer> target = ResolveTargetPlayer(line, engine);
+ if (target == nullptr)
+ {
+ if (line.tokens.size() >= 3)
+ {
+ engine->LogWarn("Unknown player: " + line.tokens[2]);
+ }
+ else
+ {
+ engine->LogWarn(std::string("Usage: ") + kExperienceUsageWithPlayer);
+ }
+ return false;
+ }
+
+ if (amount.levels)
+ {
+ target->giveExperienceLevels(amount.take ? -amount.amount : amount.amount);
+ if (amount.take)
+ {
+ engine->LogInfo("Removed " + std::to_string(amount.amount) + " level(s) from " + StringUtils::WideToUtf8(target->getName()) + ".");
+ }
+ else
+ {
+ engine->LogInfo("Added " + std::to_string(amount.amount) + " level(s) to " + StringUtils::WideToUtf8(target->getName()) + ".");
+ }
+ return true;
+ }
+
+ if (amount.take)
+ {
+ engine->LogWarn("Removing raw experience points is not supported. Use negative levels (example: xp -5L <player>).");
+ return false;
+ }
+
+ target->increaseXp(amount.amount);
+ engine->LogInfo("Added " + std::to_string(amount.amount) + " experience points to " + StringUtils::WideToUtf8(target->getName()) + ".");
+ return true;
+ }
+
+ void CliCommandExperience::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex == 1)
+ {
+ if (StringUtils::StartsWithIgnoreCase("10", context.prefix))
+ {
+ out->push_back(context.linePrefix + "10");
+ }
+ if (StringUtils::StartsWithIgnoreCase("10L", context.prefix))
+ {
+ out->push_back(context.linePrefix + "10L");
+ }
+ if (StringUtils::StartsWithIgnoreCase("-5L", context.prefix))
+ {
+ out->push_back(context.linePrefix + "-5L");
+ }
+ }
+ else if (context.currentTokenIndex == 2)
+ {
+ engine->SuggestPlayers(context.prefix, context.linePrefix, out);
+ }
+ }
+}
diff --git a/Minecraft.Server/Console/commands/experience/CliCommandExperience.h b/Minecraft.Server/Console/commands/experience/CliCommandExperience.h
new file mode 100644
index 00000000..3fddb218
--- /dev/null
+++ b/Minecraft.Server/Console/commands/experience/CliCommandExperience.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandExperience : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ std::vector<std::string> Aliases() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp b/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp
new file mode 100644
index 00000000..f41660e6
--- /dev/null
+++ b/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp
@@ -0,0 +1,109 @@
+#include "stdafx.h"
+
+#include "CliCommandGamemode.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
+#include "..\..\..\..\Minecraft.Client\PlayerList.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ constexpr const char *kGamemodeUsage = "gamemode <survival|creative|0|1> [player]";
+ constexpr const char *kGamemodeUsageWithPlayer = "gamemode <survival|creative|0|1> <player>";
+ }
+
+ const char *CliCommandGamemode::Name() const
+ {
+ return "gamemode";
+ }
+
+ std::vector<std::string> CliCommandGamemode::Aliases() const
+ {
+ return { "gm" };
+ }
+
+ const char *CliCommandGamemode::Usage() const
+ {
+ return kGamemodeUsage;
+ }
+
+ const char *CliCommandGamemode::Description() const
+ {
+ return "Set a player's game mode.";
+ }
+
+ bool CliCommandGamemode::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() < 2 || line.tokens.size() > 3)
+ {
+ engine->LogWarn(std::string("Usage: ") + kGamemodeUsage);
+ return false;
+ }
+
+ GameType *mode = engine->ParseGamemode(line.tokens[1]);
+ if (mode == nullptr)
+ {
+ engine->LogWarn("Unknown gamemode: " + line.tokens[1]);
+ return false;
+ }
+
+ std::shared_ptr<ServerPlayer> target = nullptr;
+ if (line.tokens.size() >= 3)
+ {
+ target = engine->FindPlayerByNameUtf8(line.tokens[2]);
+ if (target == nullptr)
+ {
+ engine->LogWarn("Unknown player: " + line.tokens[2]);
+ return false;
+ }
+ }
+ else
+ {
+ MinecraftServer *server = MinecraftServer::getInstance();
+ if (server == nullptr || server->getPlayers() == nullptr)
+ {
+ engine->LogWarn("Player list is not available.");
+ return false;
+ }
+
+ PlayerList *players = server->getPlayers();
+ if (players->players.size() != 1 || players->players[0] == nullptr)
+ {
+ engine->LogWarn(std::string("Usage: ") + kGamemodeUsageWithPlayer);
+ return false;
+ }
+ target = players->players[0];
+ }
+
+ target->setGameMode(mode);
+ target->fallDistance = 0.0f;
+
+ if (line.tokens.size() >= 3)
+ {
+ engine->LogInfo("Set " + line.tokens[2] + " gamemode to " + line.tokens[1] + ".");
+ }
+ else
+ {
+ engine->LogInfo("Set gamemode to " + line.tokens[1] + ".");
+ }
+ return true;
+ }
+
+ void CliCommandGamemode::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex == 1)
+ {
+ engine->SuggestGamemodes(context.prefix, context.linePrefix, out);
+ }
+ else if (context.currentTokenIndex == 2)
+ {
+ engine->SuggestPlayers(context.prefix, context.linePrefix, out);
+ }
+ }
+}
+
+
diff --git a/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.h b/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.h
new file mode 100644
index 00000000..527bb1f9
--- /dev/null
+++ b/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandGamemode : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ std::vector<std::string> Aliases() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
+ };
+}
+
diff --git a/Minecraft.Server/Console/commands/give/CliCommandGive.cpp b/Minecraft.Server/Console/commands/give/CliCommandGive.cpp
new file mode 100644
index 00000000..20c09497
--- /dev/null
+++ b/Minecraft.Server/Console/commands/give/CliCommandGive.cpp
@@ -0,0 +1,103 @@
+#include "stdafx.h"
+
+#include "CliCommandGive.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\CommandParsing.h"
+#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
+#include "..\..\..\..\Minecraft.World\GiveItemCommand.h"
+#include "..\..\..\..\Minecraft.World\net.minecraft.world.entity.player.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ constexpr const char *kGiveUsage = "give <player> <itemId> [amount] [aux]";
+ }
+
+ const char *CliCommandGive::Name() const
+ {
+ return "give";
+ }
+
+ const char *CliCommandGive::Usage() const
+ {
+ return kGiveUsage;
+ }
+
+ const char *CliCommandGive::Description() const
+ {
+ return "Give an item via Minecraft.World command dispatcher.";
+ }
+
+ bool CliCommandGive::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() < 3 || line.tokens.size() > 5)
+ {
+ engine->LogWarn(std::string("Usage: ") + kGiveUsage);
+ return false;
+ }
+
+ std::shared_ptr<ServerPlayer> target = engine->FindPlayerByNameUtf8(line.tokens[1]);
+ if (target == nullptr)
+ {
+ engine->LogWarn("Unknown player: " + line.tokens[1]);
+ return false;
+ }
+
+ int itemId = 0;
+ int amount = 1;
+ int aux = 0;
+ if (!CommandParsing::TryParseInt(line.tokens[2], &itemId))
+ {
+ engine->LogWarn("Invalid item id: " + line.tokens[2]);
+ return false;
+ }
+ if (itemId <= 0)
+ {
+ engine->LogWarn("Item id must be greater than 0.");
+ return false;
+ }
+ if (line.tokens.size() >= 4 && !CommandParsing::TryParseInt(line.tokens[3], &amount))
+ {
+ engine->LogWarn("Invalid amount: " + line.tokens[3]);
+ return false;
+ }
+ if (line.tokens.size() >= 5 && !CommandParsing::TryParseInt(line.tokens[4], &aux))
+ {
+ engine->LogWarn("Invalid aux value: " + line.tokens[4]);
+ return false;
+ }
+ if (amount <= 0)
+ {
+ engine->LogWarn("Amount must be greater than 0.");
+ return false;
+ }
+
+ std::shared_ptr<Player> player = std::dynamic_pointer_cast<Player>(target);
+ if (player == nullptr)
+ {
+ engine->LogWarn("Cannot resolve target player entity.");
+ return false;
+ }
+
+ std::shared_ptr<GameCommandPacket> packet = GiveItemCommand::preparePacket(player, itemId, amount, aux);
+ if (packet == nullptr)
+ {
+ engine->LogError("Failed to build give command packet.");
+ return false;
+ }
+
+ return engine->DispatchWorldCommand(packet->command, packet->data);
+ }
+
+ void CliCommandGive::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex == 1)
+ {
+ engine->SuggestPlayers(context.prefix, context.linePrefix, out);
+ }
+ }
+}
diff --git a/Minecraft.Server/Console/commands/give/CliCommandGive.h b/Minecraft.Server/Console/commands/give/CliCommandGive.h
new file mode 100644
index 00000000..7c21d997
--- /dev/null
+++ b/Minecraft.Server/Console/commands/give/CliCommandGive.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandGive : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/help/CliCommandHelp.cpp b/Minecraft.Server/Console/commands/help/CliCommandHelp.cpp
new file mode 100644
index 00000000..d4106a9c
--- /dev/null
+++ b/Minecraft.Server/Console/commands/help/CliCommandHelp.cpp
@@ -0,0 +1,46 @@
+#include "stdafx.h"
+
+#include "CliCommandHelp.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliRegistry.h"
+
+namespace ServerRuntime
+{
+ const char *CliCommandHelp::Name() const
+ {
+ return "help";
+ }
+
+ std::vector<std::string> CliCommandHelp::Aliases() const
+ {
+ return { "?" };
+ }
+
+ const char *CliCommandHelp::Usage() const
+ {
+ return "help";
+ }
+
+ const char *CliCommandHelp::Description() const
+ {
+ return "Show available server console commands.";
+ }
+
+ bool CliCommandHelp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ (void)line;
+ const std::vector<std::unique_ptr<IServerCliCommand>> &commands = engine->Registry().Commands();
+ engine->LogInfo("Available commands:");
+ for (size_t i = 0; i < commands.size(); ++i)
+ {
+ std::string row = " ";
+ row += commands[i]->Usage();
+ row += " - ";
+ row += commands[i]->Description();
+ engine->LogInfo(row);
+ }
+ return true;
+ }
+}
+
diff --git a/Minecraft.Server/Console/commands/help/CliCommandHelp.h b/Minecraft.Server/Console/commands/help/CliCommandHelp.h
new file mode 100644
index 00000000..3612442f
--- /dev/null
+++ b/Minecraft.Server/Console/commands/help/CliCommandHelp.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandHelp : public IServerCliCommand
+ {
+ public:
+ virtual const char *Name() const;
+ virtual std::vector<std::string> Aliases() const;
+ virtual const char *Usage() const;
+ virtual const char *Description() const;
+ virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
+ };
+}
+
diff --git a/Minecraft.Server/Console/commands/kill/CliCommandKill.cpp b/Minecraft.Server/Console/commands/kill/CliCommandKill.cpp
new file mode 100644
index 00000000..04b2c419
--- /dev/null
+++ b/Minecraft.Server/Console/commands/kill/CliCommandKill.cpp
@@ -0,0 +1,64 @@
+#include "stdafx.h"
+
+#include "CliCommandKill.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\..\Minecraft.World\CommandSender.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ constexpr const char *kKillUsage = "kill <player>";
+ }
+
+ const char *CliCommandKill::Name() const
+ {
+ return "kill";
+ }
+
+ const char *CliCommandKill::Usage() const
+ {
+ return kKillUsage;
+ }
+
+ const char *CliCommandKill::Description() const
+ {
+ return "Kill a player via Minecraft.World command dispatcher.";
+ }
+
+ bool CliCommandKill::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() != 2)
+ {
+ engine->LogWarn(std::string("Usage: ") + kKillUsage);
+ return false;
+ }
+
+ std::shared_ptr<ServerPlayer> target = engine->FindPlayerByNameUtf8(line.tokens[1]);
+ if (target == nullptr)
+ {
+ engine->LogWarn("Unknown player: " + line.tokens[1]);
+ return false;
+ }
+
+ std::shared_ptr<CommandSender> sender = std::dynamic_pointer_cast<CommandSender>(target);
+ if (sender == nullptr)
+ {
+ engine->LogWarn("Cannot resolve target command sender.");
+ return false;
+ }
+
+ return engine->DispatchWorldCommand(eGameCommand_Kill, byteArray(), sender);
+ }
+
+ void CliCommandKill::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex == 1)
+ {
+ engine->SuggestPlayers(context.prefix, context.linePrefix, out);
+ }
+ }
+}
diff --git a/Minecraft.Server/Console/commands/kill/CliCommandKill.h b/Minecraft.Server/Console/commands/kill/CliCommandKill.h
new file mode 100644
index 00000000..e558fac0
--- /dev/null
+++ b/Minecraft.Server/Console/commands/kill/CliCommandKill.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandKill : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/list/CliCommandList.cpp b/Minecraft.Server/Console/commands/list/CliCommandList.cpp
new file mode 100644
index 00000000..a9c5a212
--- /dev/null
+++ b/Minecraft.Server/Console/commands/list/CliCommandList.cpp
@@ -0,0 +1,49 @@
+#include "stdafx.h"
+
+#include "CliCommandList.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\..\Common\StringUtils.h"
+#include "..\..\..\..\Minecraft.Client\MinecraftServer.h"
+#include "..\..\..\..\Minecraft.Client\PlayerList.h"
+
+namespace ServerRuntime
+{
+ const char *CliCommandList::Name() const
+ {
+ return "list";
+ }
+
+ const char *CliCommandList::Usage() const
+ {
+ return "list";
+ }
+
+ const char *CliCommandList::Description() const
+ {
+ return "List connected players.";
+ }
+
+ bool CliCommandList::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ (void)line;
+ MinecraftServer *server = MinecraftServer::getInstance();
+ if (server == NULL || server->getPlayers() == NULL)
+ {
+ engine->LogWarn("Player list is not available.");
+ return false;
+ }
+
+ PlayerList *players = server->getPlayers();
+ std::string names = StringUtils::WideToUtf8(players->getPlayerNames());
+ if (names.empty())
+ {
+ names = "(none)";
+ }
+
+ engine->LogInfo("Players (" + std::to_string(players->getPlayerCount()) + "): " + names);
+ return true;
+ }
+}
+
+
diff --git a/Minecraft.Server/Console/commands/list/CliCommandList.h b/Minecraft.Server/Console/commands/list/CliCommandList.h
new file mode 100644
index 00000000..ad26dcbc
--- /dev/null
+++ b/Minecraft.Server/Console/commands/list/CliCommandList.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandList : public IServerCliCommand
+ {
+ public:
+ virtual const char *Name() const;
+ virtual const char *Usage() const;
+ virtual const char *Description() const;
+ virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
+ };
+}
+
diff --git a/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp b/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp
new file mode 100644
index 00000000..3517dbd8
--- /dev/null
+++ b/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp
@@ -0,0 +1,98 @@
+#include "stdafx.h"
+
+#include "CliCommandPardonIp.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\Access\Access.h"
+#include "..\..\..\Common\NetworkUtils.h"
+#include "..\..\..\Common\StringUtils.h"
+
+namespace ServerRuntime
+{
+ const char *CliCommandPardonIp::Name() const
+ {
+ return "pardon-ip";
+ }
+
+ const char *CliCommandPardonIp::Usage() const
+ {
+ return "pardon-ip <address>";
+ }
+
+ const char *CliCommandPardonIp::Description() const
+ {
+ return "Remove an IP ban.";
+ }
+
+ /**
+ * Validates the literal IP argument and removes the matching Access IP ban entry
+ * リテラルIPを検証して一致するIP BANを解除する
+ */
+ bool CliCommandPardonIp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() != 2)
+ {
+ engine->LogWarn("Usage: pardon-ip <address>");
+ return false;
+ }
+ if (!ServerRuntime::Access::IsInitialized())
+ {
+ engine->LogWarn("Access manager is not initialized.");
+ return false;
+ }
+
+ // Java Edition pardon-ip only operates on a literal address, so do not resolve player names here.
+ const std::string ip = StringUtils::TrimAscii(line.tokens[1]);
+ if (!NetworkUtils::IsIpLiteral(ip))
+ {
+ engine->LogWarn("Invalid IP address: " + line.tokens[1]);
+ return false;
+ }
+ // Distinguish invalid input from a valid but currently unbanned address for clearer operator feedback.
+ if (!ServerRuntime::Access::IsIpBanned(ip))
+ {
+ engine->LogWarn("That IP address is not banned.");
+ return false;
+ }
+ if (!ServerRuntime::Access::RemoveIpBan(ip))
+ {
+ engine->LogError("Failed to remove IP ban.");
+ return false;
+ }
+
+ engine->LogInfo("Unbanned IP address " + ip + ".");
+ return true;
+ }
+
+ /**
+ * Suggests currently banned IP addresses for the Java Edition literal-IP argument
+ * BAN済みIPの補完候補を返す
+ */
+ void CliCommandPardonIp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ (void)engine;
+ // Complete from the persisted IP-ban snapshot because this command only accepts already-banned literals.
+ if (context.currentTokenIndex != 1 || out == nullptr)
+ {
+ return;
+ }
+
+ std::vector<ServerRuntime::Access::BannedIpEntry> entries;
+ if (!ServerRuntime::Access::SnapshotBannedIps(&entries))
+ {
+ return;
+ }
+
+ // Reuse the normalized prefix match used by other commands so completion stays case-insensitive.
+ for (const auto &entry : entries)
+ {
+ const std::string &candidate = entry.ip;
+ if (StringUtils::StartsWithIgnoreCase(candidate, context.prefix))
+ {
+ out->push_back(context.linePrefix + candidate);
+ }
+ }
+ }
+}
+
diff --git a/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.h b/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.h
new file mode 100644
index 00000000..96f4c7fc
--- /dev/null
+++ b/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ /**
+ * Removes a dedicated-server IP ban using Java Edition style syntax and Access-backed persistence
+ */
+ class CliCommandPardonIp : public IServerCliCommand
+ {
+ public:
+ virtual const char *Name() const;
+ virtual const char *Usage() const;
+ virtual const char *Description() const;
+ virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
+ virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp b/Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp
new file mode 100644
index 00000000..d1e995e9
--- /dev/null
+++ b/Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp
@@ -0,0 +1,173 @@
+#include "stdafx.h"
+
+#include "CliCommandPardon.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\Access\Access.h"
+#include "..\..\..\Common\StringUtils.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+
+#include <algorithm>
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ static void AppendUniqueText(const std::string &text, std::vector<std::string> *out)
+ {
+ if (out == nullptr || text.empty())
+ {
+ return;
+ }
+
+ if (std::find(out->begin(), out->end(), text) == out->end())
+ {
+ out->push_back(text);
+ }
+ }
+
+ static void AppendUniqueXuid(PlayerUID xuid, std::vector<PlayerUID> *out)
+ {
+ if (out == nullptr || xuid == INVALID_XUID)
+ {
+ return;
+ }
+
+ if (std::find(out->begin(), out->end(), xuid) == out->end())
+ {
+ out->push_back(xuid);
+ }
+ }
+ }
+
+ const char *CliCommandPardon::Name() const
+ {
+ return "pardon";
+ }
+
+ const char *CliCommandPardon::Usage() const
+ {
+ return "pardon <player>";
+ }
+
+ const char *CliCommandPardon::Description() const
+ {
+ return "Remove a player ban.";
+ }
+
+ /**
+ * Removes every Access ban entry that matches the requested player name so dual-XUID entries are cleared together
+ * 名前に一致するBANをまとめて解除する
+ */
+ bool CliCommandPardon::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() != 2)
+ {
+ engine->LogWarn("Usage: pardon <player>");
+ return false;
+ }
+ if (!ServerRuntime::Access::IsInitialized())
+ {
+ engine->LogWarn("Access manager is not initialized.");
+ return false;
+ }
+
+ std::vector<PlayerUID> xuidsToRemove;
+ std::vector<std::string> matchedNames;
+ std::shared_ptr<ServerPlayer> onlineTarget = engine->FindPlayerByNameUtf8(line.tokens[1]);
+ if (onlineTarget != nullptr)
+ {
+ if (ServerRuntime::Access::IsPlayerBanned(onlineTarget->getXuid()))
+ {
+ AppendUniqueXuid(onlineTarget->getXuid(), &xuidsToRemove);
+ }
+ if (ServerRuntime::Access::IsPlayerBanned(onlineTarget->getOnlineXuid()))
+ {
+ AppendUniqueXuid(onlineTarget->getOnlineXuid(), &xuidsToRemove);
+ }
+ }
+
+ std::vector<ServerRuntime::Access::BannedPlayerEntry> entries;
+ if (!ServerRuntime::Access::SnapshotBannedPlayers(&entries))
+ {
+ engine->LogError("Failed to read banned players.");
+ return false;
+ }
+
+ const std::string loweredTarget = StringUtils::ToLowerAscii(line.tokens[1]);
+ for (const auto &entry : entries)
+ {
+ if (StringUtils::ToLowerAscii(entry.name) == loweredTarget)
+ {
+ PlayerUID parsedXuid = INVALID_XUID;
+ if (ServerRuntime::Access::TryParseXuid(entry.xuid, &parsedXuid))
+ {
+ AppendUniqueXuid(parsedXuid, &xuidsToRemove);
+ }
+ AppendUniqueText(entry.name, &matchedNames);
+ }
+ }
+
+ if (xuidsToRemove.empty())
+ {
+ engine->LogWarn("That player is not banned.");
+ return false;
+ }
+
+ for (const auto xuid : xuidsToRemove)
+ {
+ if (!ServerRuntime::Access::RemovePlayerBan(xuid))
+ {
+ engine->LogError("Failed to remove player ban.");
+ return false;
+ }
+ }
+
+ std::string playerName = line.tokens[1];
+ if (!matchedNames.empty())
+ {
+ playerName = matchedNames[0];
+ }
+ else if (onlineTarget != nullptr)
+ {
+ playerName = StringUtils::WideToUtf8(onlineTarget->getName());
+ }
+
+ engine->LogInfo("Unbanned player " + playerName + ".");
+ return true;
+ }
+
+ /**
+ * Suggests currently banned player names first and then online names for convenience
+ * BAN済み名とオンライン名を補完候補に出す
+ */
+ void CliCommandPardon::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex != 1 || out == nullptr)
+ {
+ return;
+ }
+
+ std::vector<ServerRuntime::Access::BannedPlayerEntry> entries;
+ if (ServerRuntime::Access::SnapshotBannedPlayers(&entries))
+ {
+ std::vector<std::string> names;
+ for (const auto &entry : entries)
+ {
+ AppendUniqueText(entry.name, &names);
+ }
+
+ for (const auto &name : names)
+ {
+ if (StringUtils::StartsWithIgnoreCase(name, context.prefix))
+ {
+ out->push_back(context.linePrefix + name);
+ }
+ }
+ }
+
+ engine->SuggestPlayers(context.prefix, context.linePrefix, out);
+ }
+}
+
diff --git a/Minecraft.Server/Console/commands/pardon/CliCommandPardon.h b/Minecraft.Server/Console/commands/pardon/CliCommandPardon.h
new file mode 100644
index 00000000..a171d428
--- /dev/null
+++ b/Minecraft.Server/Console/commands/pardon/CliCommandPardon.h
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ /**
+ * Removes dedicated-server player bans using Java Edition style syntax and Access-backed persistence
+ */
+ class CliCommandPardon : public IServerCliCommand
+ {
+ public:
+ virtual const char *Name() const;
+ virtual const char *Usage() const;
+ virtual const char *Description() const;
+ virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
+ virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/stop/CliCommandStop.cpp b/Minecraft.Server/Console/commands/stop/CliCommandStop.cpp
new file mode 100644
index 00000000..29e42cd9
--- /dev/null
+++ b/Minecraft.Server/Console/commands/stop/CliCommandStop.cpp
@@ -0,0 +1,32 @@
+#include "stdafx.h"
+
+#include "CliCommandStop.h"
+
+#include "..\..\ServerCliEngine.h"
+
+namespace ServerRuntime
+{
+ const char *CliCommandStop::Name() const
+ {
+ return "stop";
+ }
+
+ const char *CliCommandStop::Usage() const
+ {
+ return "stop";
+ }
+
+ const char *CliCommandStop::Description() const
+ {
+ return "Stop the dedicated server.";
+ }
+
+ bool CliCommandStop::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ (void)line;
+ engine->LogInfo("Stopping server...");
+ engine->RequestShutdown();
+ return true;
+ }
+}
+
diff --git a/Minecraft.Server/Console/commands/stop/CliCommandStop.h b/Minecraft.Server/Console/commands/stop/CliCommandStop.h
new file mode 100644
index 00000000..2297c673
--- /dev/null
+++ b/Minecraft.Server/Console/commands/stop/CliCommandStop.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandStop : public IServerCliCommand
+ {
+ public:
+ virtual const char *Name() const;
+ virtual const char *Usage() const;
+ virtual const char *Description() const;
+ virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine);
+ };
+}
+
diff --git a/Minecraft.Server/Console/commands/time/CliCommandTime.cpp b/Minecraft.Server/Console/commands/time/CliCommandTime.cpp
new file mode 100644
index 00000000..d274993c
--- /dev/null
+++ b/Minecraft.Server/Console/commands/time/CliCommandTime.cpp
@@ -0,0 +1,118 @@
+#include "stdafx.h"
+
+#include "CliCommandTime.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\Common\StringUtils.h"
+#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
+#include "..\..\..\..\Minecraft.World\TimeCommand.h"
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ constexpr const char *kTimeUsage = "time <day|night|set day|set night>";
+
+ static bool TryResolveNightFlag(const std::vector<std::string> &tokens, bool *outNight)
+ {
+ if (outNight == nullptr)
+ {
+ return false;
+ }
+
+ std::string value;
+ if (tokens.size() == 2)
+ {
+ value = StringUtils::ToLowerAscii(tokens[1]);
+ }
+ else if (tokens.size() == 3 && StringUtils::ToLowerAscii(tokens[1]) == "set")
+ {
+ value = StringUtils::ToLowerAscii(tokens[2]);
+ }
+ else
+ {
+ return false;
+ }
+
+ if (value == "day")
+ {
+ *outNight = false;
+ return true;
+ }
+ if (value == "night")
+ {
+ *outNight = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ static void SuggestLiteral(const char *candidate, const ServerCliCompletionContext &context, std::vector<std::string> *out)
+ {
+ if (candidate == nullptr || out == nullptr)
+ {
+ return;
+ }
+
+ const std::string text(candidate);
+ if (StringUtils::StartsWithIgnoreCase(text, context.prefix))
+ {
+ out->push_back(context.linePrefix + text);
+ }
+ }
+ }
+
+ const char *CliCommandTime::Name() const
+ {
+ return "time";
+ }
+
+ const char *CliCommandTime::Usage() const
+ {
+ return kTimeUsage;
+ }
+
+ const char *CliCommandTime::Description() const
+ {
+ return "Set day or night via Minecraft.World command dispatcher.";
+ }
+
+ bool CliCommandTime::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ bool night = false;
+ if (!TryResolveNightFlag(line.tokens, &night))
+ {
+ engine->LogWarn(std::string("Usage: ") + kTimeUsage);
+ return false;
+ }
+
+ std::shared_ptr<GameCommandPacket> packet = TimeCommand::preparePacket(night);
+ if (packet == nullptr)
+ {
+ engine->LogError("Failed to build time command packet.");
+ return false;
+ }
+
+ return engine->DispatchWorldCommand(packet->command, packet->data);
+ }
+
+ void CliCommandTime::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ (void)engine;
+ if (context.currentTokenIndex == 1)
+ {
+ SuggestLiteral("day", context, out);
+ SuggestLiteral("night", context, out);
+ SuggestLiteral("set", context, out);
+ }
+ else if (context.currentTokenIndex == 2 &&
+ context.parsed.tokens.size() >= 2 &&
+ StringUtils::ToLowerAscii(context.parsed.tokens[1]) == "set")
+ {
+ SuggestLiteral("day", context, out);
+ SuggestLiteral("night", context, out);
+ }
+ }
+}
diff --git a/Minecraft.Server/Console/commands/time/CliCommandTime.h b/Minecraft.Server/Console/commands/time/CliCommandTime.h
new file mode 100644
index 00000000..28cf5e5a
--- /dev/null
+++ b/Minecraft.Server/Console/commands/time/CliCommandTime.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandTime : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/tp/CliCommandTp.cpp b/Minecraft.Server/Console/commands/tp/CliCommandTp.cpp
new file mode 100644
index 00000000..45dbb284
--- /dev/null
+++ b/Minecraft.Server/Console/commands/tp/CliCommandTp.cpp
@@ -0,0 +1,82 @@
+#include "stdafx.h"
+
+#include "CliCommandTp.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\..\Minecraft.Client\PlayerConnection.h"
+#include "..\..\..\..\Minecraft.Client\TeleportCommand.h"
+#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
+#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ constexpr const char *kTpUsage = "tp <player> <target>";
+ }
+
+ const char *CliCommandTp::Name() const
+ {
+ return "tp";
+ }
+
+ std::vector<std::string> CliCommandTp::Aliases() const
+ {
+ return { "teleport" };
+ }
+
+ const char *CliCommandTp::Usage() const
+ {
+ return kTpUsage;
+ }
+
+ const char *CliCommandTp::Description() const
+ {
+ return "Teleport one player to another via Minecraft.World command dispatcher.";
+ }
+
+ bool CliCommandTp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() != 3)
+ {
+ engine->LogWarn(std::string("Usage: ") + kTpUsage);
+ return false;
+ }
+
+ std::shared_ptr<ServerPlayer> subject = engine->FindPlayerByNameUtf8(line.tokens[1]);
+ std::shared_ptr<ServerPlayer> destination = engine->FindPlayerByNameUtf8(line.tokens[2]);
+ if (subject == nullptr)
+ {
+ engine->LogWarn("Unknown player: " + line.tokens[1]);
+ return false;
+ }
+ if (destination == nullptr)
+ {
+ engine->LogWarn("Unknown player: " + line.tokens[2]);
+ return false;
+ }
+ if (subject->connection == nullptr)
+ {
+ engine->LogWarn("Cannot teleport because source player connection is inactive.");
+ return false;
+ }
+ std::shared_ptr<GameCommandPacket> packet = TeleportCommand::preparePacket(subject->getXuid(), destination->getXuid());
+ if (packet == nullptr)
+ {
+ engine->LogError("Failed to build teleport command packet.");
+ return false;
+ }
+
+ return engine->DispatchWorldCommand(packet->command, packet->data);
+ }
+
+ void CliCommandTp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ if (context.currentTokenIndex == 1 || context.currentTokenIndex == 2)
+ {
+ engine->SuggestPlayers(context.prefix, context.linePrefix, out);
+ }
+ }
+}
+
diff --git a/Minecraft.Server/Console/commands/tp/CliCommandTp.h b/Minecraft.Server/Console/commands/tp/CliCommandTp.h
new file mode 100644
index 00000000..6e9ffdd7
--- /dev/null
+++ b/Minecraft.Server/Console/commands/tp/CliCommandTp.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandTp : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ std::vector<std::string> Aliases() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
+ };
+}
+
diff --git a/Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp b/Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp
new file mode 100644
index 00000000..e7f01954
--- /dev/null
+++ b/Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp
@@ -0,0 +1,49 @@
+#include "stdafx.h"
+
+#include "CliCommandWeather.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\..\Minecraft.World\GameCommandPacket.h"
+#include "..\..\..\..\Minecraft.World\ToggleDownfallCommand.h"
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ constexpr const char *kWeatherUsage = "weather";
+ }
+
+ const char *CliCommandWeather::Name() const
+ {
+ return "weather";
+ }
+
+ const char *CliCommandWeather::Usage() const
+ {
+ return kWeatherUsage;
+ }
+
+ const char *CliCommandWeather::Description() const
+ {
+ return "Toggle weather via Minecraft.World command dispatcher.";
+ }
+
+ bool CliCommandWeather::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() != 1)
+ {
+ engine->LogWarn(std::string("Usage: ") + kWeatherUsage);
+ return false;
+ }
+
+ std::shared_ptr<GameCommandPacket> packet = ToggleDownfallCommand::preparePacket();
+ if (packet == nullptr)
+ {
+ engine->LogError("Failed to build weather command packet.");
+ return false;
+ }
+
+ return engine->DispatchWorldCommand(packet->command, packet->data);
+ }
+}
diff --git a/Minecraft.Server/Console/commands/weather/CliCommandWeather.h b/Minecraft.Server/Console/commands/weather/CliCommandWeather.h
new file mode 100644
index 00000000..03498b47
--- /dev/null
+++ b/Minecraft.Server/Console/commands/weather/CliCommandWeather.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandWeather : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ };
+}
diff --git a/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp b/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp
new file mode 100644
index 00000000..03724278
--- /dev/null
+++ b/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp
@@ -0,0 +1,285 @@
+#include "stdafx.h"
+
+#include "CliCommandWhitelist.h"
+
+#include "..\..\ServerCliEngine.h"
+#include "..\..\ServerCliParser.h"
+#include "..\..\..\Access\Access.h"
+#include "..\..\..\Common\StringUtils.h"
+#include "..\..\..\ServerProperties.h"
+
+#include <algorithm>
+#include <array>
+
+namespace ServerRuntime
+{
+ namespace
+ {
+ static const char *kWhitelistUsage = "whitelist <on|off|list|add|remove|reload> [...]";
+
+ static bool CompareWhitelistEntries(const ServerRuntime::Access::WhitelistedPlayerEntry &left, const ServerRuntime::Access::WhitelistedPlayerEntry &right)
+ {
+ const auto leftName = StringUtils::ToLowerAscii(left.name);
+ const auto rightName = StringUtils::ToLowerAscii(right.name);
+ if (leftName != rightName)
+ {
+ return leftName < rightName;
+ }
+
+ return StringUtils::ToLowerAscii(left.xuid) < StringUtils::ToLowerAscii(right.xuid);
+ }
+
+ static bool PersistWhitelistToggle(bool enabled)
+ {
+ auto config = LoadServerPropertiesConfig();
+ config.whiteListEnabled = enabled;
+ return SaveServerPropertiesConfig(config);
+ }
+
+ static std::string BuildWhitelistEntryRow(const ServerRuntime::Access::WhitelistedPlayerEntry &entry)
+ {
+ std::string row = " ";
+ row += entry.xuid;
+ if (!entry.name.empty())
+ {
+ row += " - ";
+ row += entry.name;
+ }
+ return row;
+ }
+
+ static void LogWhitelistMode(ServerCliEngine *engine)
+ {
+ engine->LogInfo(std::string("Whitelist is ") + (ServerRuntime::Access::IsWhitelistEnabled() ? "enabled." : "disabled."));
+ }
+
+ static bool LogWhitelistEntries(ServerCliEngine *engine)
+ {
+ std::vector<ServerRuntime::Access::WhitelistedPlayerEntry> entries;
+ if (!ServerRuntime::Access::SnapshotWhitelistedPlayers(&entries))
+ {
+ engine->LogError("Failed to read whitelist entries.");
+ return false;
+ }
+
+ std::sort(entries.begin(), entries.end(), CompareWhitelistEntries);
+ LogWhitelistMode(engine);
+ engine->LogInfo("There are " + std::to_string(entries.size()) + " whitelisted player(s).");
+ for (const auto &entry : entries)
+ {
+ engine->LogInfo(BuildWhitelistEntryRow(entry));
+ }
+ return true;
+ }
+
+ static bool TryParseWhitelistXuid(const std::string &text, ServerCliEngine *engine, PlayerUID *outXuid)
+ {
+ if (ServerRuntime::Access::TryParseXuid(text, outXuid))
+ {
+ return true;
+ }
+
+ engine->LogWarn("Invalid XUID: " + text);
+ return false;
+ }
+
+ static void SuggestLiteral(const std::string &candidate, const ServerCliCompletionContext &context, std::vector<std::string> *out)
+ {
+ if (out == nullptr)
+ {
+ return;
+ }
+
+ if (StringUtils::StartsWithIgnoreCase(candidate, context.prefix))
+ {
+ out->push_back(context.linePrefix + candidate);
+ }
+ }
+ }
+
+ const char *CliCommandWhitelist::Name() const
+ {
+ return "whitelist";
+ }
+
+ const char *CliCommandWhitelist::Usage() const
+ {
+ return kWhitelistUsage;
+ }
+
+ const char *CliCommandWhitelist::Description() const
+ {
+ return "Manage the dedicated-server XUID whitelist.";
+ }
+
+ bool CliCommandWhitelist::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
+ {
+ if (line.tokens.size() < 2)
+ {
+ engine->LogWarn(std::string("Usage: ") + kWhitelistUsage);
+ return false;
+ }
+ if (!ServerRuntime::Access::IsInitialized())
+ {
+ engine->LogWarn("Access manager is not initialized.");
+ return false;
+ }
+
+ const auto subcommand = StringUtils::ToLowerAscii(line.tokens[1]);
+ if (subcommand == "on" || subcommand == "off")
+ {
+ if (line.tokens.size() != 2)
+ {
+ engine->LogWarn("Usage: whitelist <on|off>");
+ return false;
+ }
+
+ const bool enabled = (subcommand == "on");
+ if (!PersistWhitelistToggle(enabled))
+ {
+ engine->LogError("Failed to persist whitelist mode to server.properties.");
+ return false;
+ }
+
+ ServerRuntime::Access::SetWhitelistEnabled(enabled);
+ engine->LogInfo(std::string("Whitelist ") + (enabled ? "enabled." : "disabled."));
+ return true;
+ }
+
+ if (subcommand == "list")
+ {
+ if (line.tokens.size() != 2)
+ {
+ engine->LogWarn("Usage: whitelist list");
+ return false;
+ }
+
+ return LogWhitelistEntries(engine);
+ }
+
+ if (subcommand == "reload")
+ {
+ if (line.tokens.size() != 2)
+ {
+ engine->LogWarn("Usage: whitelist reload");
+ return false;
+ }
+ if (!ServerRuntime::Access::ReloadWhitelist())
+ {
+ engine->LogError("Failed to reload whitelist.");
+ return false;
+ }
+
+ const auto config = LoadServerPropertiesConfig();
+ ServerRuntime::Access::SetWhitelistEnabled(config.whiteListEnabled);
+ engine->LogInfo("Reloaded whitelist from disk.");
+ LogWhitelistMode(engine);
+ return true;
+ }
+
+ if (subcommand == "add")
+ {
+ if (line.tokens.size() < 3)
+ {
+ engine->LogWarn("Usage: whitelist add <xuid> [name ...]");
+ return false;
+ }
+
+ PlayerUID xuid = INVALID_XUID;
+ if (!TryParseWhitelistXuid(line.tokens[2], engine, &xuid))
+ {
+ return false;
+ }
+
+ if (ServerRuntime::Access::IsPlayerWhitelisted(xuid))
+ {
+ engine->LogWarn("That XUID is already whitelisted.");
+ return false;
+ }
+
+ const auto metadata = ServerRuntime::Access::WhitelistManager::BuildDefaultMetadata("Console");
+ const auto name = StringUtils::JoinTokens(line.tokens, 3);
+ if (!ServerRuntime::Access::AddWhitelistedPlayer(xuid, name, metadata))
+ {
+ engine->LogError("Failed to write whitelist entry.");
+ return false;
+ }
+
+ std::string message = "Whitelisted XUID " + ServerRuntime::Access::FormatXuid(xuid) + ".";
+ if (!name.empty())
+ {
+ message += " Name: " + name;
+ }
+ engine->LogInfo(message);
+ return true;
+ }
+
+ if (subcommand == "remove")
+ {
+ if (line.tokens.size() != 3)
+ {
+ engine->LogWarn("Usage: whitelist remove <xuid>");
+ return false;
+ }
+
+ PlayerUID xuid = INVALID_XUID;
+ if (!TryParseWhitelistXuid(line.tokens[2], engine, &xuid))
+ {
+ return false;
+ }
+
+ if (!ServerRuntime::Access::IsPlayerWhitelisted(xuid))
+ {
+ engine->LogWarn("That XUID is not whitelisted.");
+ return false;
+ }
+
+ if (!ServerRuntime::Access::RemoveWhitelistedPlayer(xuid))
+ {
+ engine->LogError("Failed to remove whitelist entry.");
+ return false;
+ }
+
+ engine->LogInfo("Removed XUID " + ServerRuntime::Access::FormatXuid(xuid) + " from the whitelist.");
+ return true;
+ }
+
+ engine->LogWarn(std::string("Usage: ") + kWhitelistUsage);
+ return false;
+ }
+
+ void CliCommandWhitelist::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
+ {
+ (void)engine;
+ if (out == nullptr)
+ {
+ return;
+ }
+
+ if (context.currentTokenIndex == 1)
+ {
+ SuggestLiteral("on", context, out);
+ SuggestLiteral("off", context, out);
+ SuggestLiteral("list", context, out);
+ SuggestLiteral("add", context, out);
+ SuggestLiteral("remove", context, out);
+ SuggestLiteral("reload", context, out);
+ return;
+ }
+
+ if (context.currentTokenIndex == 2 && context.parsed.tokens.size() >= 2 && StringUtils::ToLowerAscii(context.parsed.tokens[1]) == "remove")
+ {
+ std::vector<ServerRuntime::Access::WhitelistedPlayerEntry> entries;
+ if (!ServerRuntime::Access::SnapshotWhitelistedPlayers(&entries))
+ {
+ return;
+ }
+
+ for (const auto &entry : entries)
+ {
+ SuggestLiteral(entry.xuid, context, out);
+ }
+ }
+ }
+}
+
diff --git a/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.h b/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.h
new file mode 100644
index 00000000..45e21a5e
--- /dev/null
+++ b/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "..\IServerCliCommand.h"
+
+namespace ServerRuntime
+{
+ class CliCommandWhitelist : public IServerCliCommand
+ {
+ public:
+ const char *Name() const override;
+ const char *Usage() const override;
+ const char *Description() const override;
+ bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override;
+ void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const override;
+ };
+}
+
diff --git a/Minecraft.Server/Minecraft.Server.vcxproj b/Minecraft.Server/Minecraft.Server.vcxproj
new file mode 100644
index 00000000..be2eb80c
--- /dev/null
+++ b/Minecraft.Server/Minecraft.Server.vcxproj
@@ -0,0 +1,749 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{7CB40BFC-C8E4-4293-A22E-D2041348D5AF}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>MinecraftServer</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v143</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
+ </ImportGroup>
+ <ImportGroup Label="Shared" />
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\obj\MinecraftServer\</IntDir>
+ <TargetName>Minecraft.Server</TargetName>
+ <LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
+ <LocalDebuggerCommandArguments>-port 25565 -bind 0.0.0.0 -name DedicatedServer</LocalDebuggerCommandArguments>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\obj\MinecraftServer\</IntDir>
+ <TargetName>Minecraft.Server</TargetName>
+ <LocalDebuggerWorkingDirectory>$(OutDir)</LocalDebuggerWorkingDirectory>
+ <LocalDebuggerCommandArguments>-port 25565 -bind 0.0.0.0 -name DedicatedServer</LocalDebuggerCommandArguments>
+ <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderOutputFile>$(OutDir)MinecraftServer.pch</PrecompiledHeaderOutputFile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;$(ProjectDir)Windows64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <MASM>
+ <UseSafeExceptionHandlers>false</UseSafeExceptionHandlers>
+ </MASM>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Xbox;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>d3d11.lib;XInput9_1_0.lib;wsock32.lib;legacy_stdio_definitions.lib;..\Minecraft.World\x64_Debug\Minecraft.World.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggy_w64.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggyperfmon_w64.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggyexpruntime_w64.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Input_d.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Storage_d.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Render_PC_d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent>
+ <Command>powershell -ExecutionPolicy Bypass -File "$(ProjectDir)Windows64\postbuild_server.ps1" -OutDir "$(OutDir)." -ProjectRoot "$(ProjectDir).." -Configuration "$(Configuration)"</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderOutputFile>$(OutDir)MinecraftServer.pch</PrecompiledHeaderOutputFile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <ExceptionHandling>Sync</ExceptionHandling>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;$(ProjectDir)Windows64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ClCompile>
+ <MASM>
+ <UseSafeExceptionHandlers>false</UseSafeExceptionHandlers>
+ </MASM>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WINDOWS64;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Xbox;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>d3d11.lib;XInput9_1_0.lib;wsock32.lib;legacy_stdio_definitions.lib;..\Minecraft.World\x64_Release\Minecraft.World.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggy_w64.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggyperfmon_w64.lib;..\Minecraft.Client\Windows64\Iggy\lib\iggyexpruntime_w64.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Input.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Storage.lib;..\Minecraft.Client\Windows64\4JLibs\libs\4J_Render_PC.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <PostBuildEvent>
+ <Command>powershell -ExecutionPolicy Bypass -File "$(ProjectDir)Windows64\postbuild_server.ps1" -OutDir "$(OutDir)." -ProjectRoot "$(ProjectDir).." -Configuration "$(Configuration)"</Command>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="Access\Access.cpp" />
+ <ClCompile Include="Access\BanManager.cpp" />
+ <ClCompile Include="Access\WhitelistManager.cpp" />
+ <ClCompile Include="ServerLogManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\AbstractTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\AchievementPopup.cpp" />
+ <ClCompile Include="..\Minecraft.Client\AchievementScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\AllowAllCuller.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ArchiveFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ArrowRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BatModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BatRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BeaconRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BlazeModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BlazeRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BoatModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BoatRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BookModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BossMobGuiInfo.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BreakingItemParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BubbleParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BufferedImage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Button.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Camera.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CaveSpiderRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChatScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChestModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChestRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChickenModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChickenRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Chunk.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ClientConnection.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ClientConstants.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ClockTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Audio\Consoles_SoundEngine.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Audio\SoundEngine.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Audio\SoundNames.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Colours\ColourTable.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\ConsoleGameMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Console_Utils.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Consoles_App.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCAudioFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCCapeFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCColourTableFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesHeader.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCLocalisationFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCPack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCSkinFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCTextureFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCUIDataFile.cpp" />
+ <ClCompile Include="..\include\lce_filesystem\lce_filesystem.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\AddEnchantmentRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\AddItemRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\ApplySchematicRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\BiomeOverride.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\CollectItemRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\CompleteAllRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\CompoundGameRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleGenerateStructure.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleSchematicFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRule.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerators.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRules.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRuleset.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\NamedAreaRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\StartFeature.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\UpdatePlayerRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\UseTileRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionGenerateBox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceBlock.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceContainer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceSpawner.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardInterface.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Network\GameNetworkManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Network\PlatformNetworkManagerStub.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Telemetry\TelemetryManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Trial\TrialMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaConstraint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChangeStateConstraint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChoiceTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\CompleteUsingItemTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ControllerTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\CraftTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\DiggerItemHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\EffectChangedTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorial.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialActiveTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\HorseChoiceTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\InfoTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\InputConstraint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtEntityHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtTileHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\PickupTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProcedureCompoundTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProgressFlagTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\RideEntityTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\StatTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TakeItemHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\Tutorial.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMessage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseItemTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseTileTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\XuiCraftingTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AbstractContainerMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AnvilMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BeaconMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BrewingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CommandBlockMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_ContainerMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CraftingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CreativeMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_DispenserMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_EnchantingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FireworksMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FurnaceMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HUD.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HopperMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HorseInventoryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_InventoryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_PauseMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_StartGame.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_TradingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIBitmapFont.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Chat.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIConsole.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIMarketingGuide.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Logo.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_MenuBackground.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Panorama.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_PressStartToPlay.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Tooltips.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_TutorialPopup.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Base.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BeaconEffectButton.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BitmapIcon.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Button.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_ButtonList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_CheckBox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Cursor.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DLCList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DynamicLabel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentBook.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentButton.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_HTMLLabel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Label.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_LeaderboardList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftHorse.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftPlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerSkinPreview.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Progress.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SaveList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Slider.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SlotList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SpaceIndicatorBar.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TextInput.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TexturePackList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIController.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIFontData.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIGroup.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UILayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AbstractContainerMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AnvilMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BeaconMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BrewingStandMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ConnectingProgress.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ContainerMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ControlsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CraftingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreateWorldMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreativeMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Credits.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCMainMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCOffersMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DeathMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugCreateSchematic.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOptions.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOverlay.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugSetCamera.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DispenserMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EULA.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EnchantingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EndPoem.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FireworksMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FullscreenProgress.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FurnaceMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HUD.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HelpAndOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HopperMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HorseInventoryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlay.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlayMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameHostOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameInfoMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGamePlayerOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Intro.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InventoryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_JoinMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Keyboard.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LanguageSelector.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LaunchMoreOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LeaderboardsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadOrJoinMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MainMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MessageBox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_NewUpdateMessage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_PauseMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_QuadrantSignin.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ReinstallMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SaveMessage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsAudioMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsControlMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsGraphicsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsUIMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SignEntryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SkinSelectMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TeleportMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Timer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TradingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TrialExitUpsell.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIString.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UITTFFont.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\adler32.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\compress.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\crc32.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\deflate.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\gzclose.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\gzlib.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\gzread.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\gzwrite.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\infback.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\inffast.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\inflate.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\inftrees.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\trees.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\uncompr.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\zutil.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\CompassTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ConfirmScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ConsoleInput.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ControlsScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CowModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CowRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CreateWorldScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CreeperModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CreeperRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CritParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CritParticle2.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Cube.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DLCTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DeathScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DefaultRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DefaultTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DemoUser.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DerivedServerLevel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DirtyChunkSorter.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DispenserBootstrap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DistanceChunkSorter.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DragonBreathParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DragonModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DripParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EchantmentTableParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EditBox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnchantTableRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderChestRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderCrystalModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderCrystalRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderDragonRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EndermanModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EndermanRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EntityRenderDispatcher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EntityRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EntityTileRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EntityTracker.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ErrorScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ExperienceOrbRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ExplodeParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Extrax64Stubs.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FallingTileRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FileTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FireballRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FireworksParticles.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FishingHookRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FlameParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FolderTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Font.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FootstepParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Frustum.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FrustumCuller.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FrustumData.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GameRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GhastModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GhastRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GiantMobRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Gui.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GuiComponent.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GuiMessage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GuiParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GuiParticles.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HeartParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HorseRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HttpTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HugeExplosionParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HugeExplosionSeedParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HumanoidMobRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HumanoidModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\InBedChatScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Input.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ItemFrameRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ItemInHandRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ItemRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ItemSpriteRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\JoinMultiplayerScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\KeyMapping.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LargeChestModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LavaParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LavaSlimeModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LavaSlimeRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LeashKnotModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LeashKnotRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LevelRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Lighting.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LightningBoltRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LivingEntityRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LocalPlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MemTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MemoryTracker.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MinecartModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MinecartRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MinecartSpawnerRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Minecraft.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MinecraftServer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Minimap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MobRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MobSkinMemTextureProcessor.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MobSkinTextureProcessor.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MobSpawnerRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Model.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ModelHorse.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ModelPart.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MultiPlayerChunkCache.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MultiPlayerGameMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MultiPlayerLevel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MultiPlayerLocalPlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MushroomCowRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\NameEntryScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\NetherPortalParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\NoteParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\OcelotModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\OcelotRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\OffsettedRenderList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Options.cpp" />
+ <ClCompile Include="..\Minecraft.Client\OptionsScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PaintingRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Particle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ParticleEngine.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PauseScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PendingConnection.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PigModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PigRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PistonPieceRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerChunkMap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerCloudParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerConnection.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Polygon.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PreStitchedTextureMap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ProgressRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\QuadrupedModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Rect2i.cpp" />
+ <ClCompile Include="..\Minecraft.Client\RedDustParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\RemotePlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\RenameWorldScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Screen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ScreenSizeCalculator.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ScrolledSelectionList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SelectWorldScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerChunkCache.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerCommandDispatcher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerConnection.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerLevel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerLevelListener.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerPlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerPlayerGameMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerScoreboard.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Settings.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SheepFurModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SheepModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SheepRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SignModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SignRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SilverfishModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SilverfishRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SimpleIcon.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkeletonHeadModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkeletonModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkeletonRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkiModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkullTileRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SlideButton.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SlimeModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SlimeRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SmallButton.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SmokeParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SnowManModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SnowManRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SnowShovelParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SpellParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SpiderModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SpiderRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SplashParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SquidModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SquidRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StatsCounter.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StatsScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StatsSyncher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StitchSlot.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StitchedTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Stitcher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StringTable.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SuspendedParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SuspendedTownParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TakeAnimationParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TeleportCommand.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TerrainParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Tesselator.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TexOffs.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Texture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TextureAtlas.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TextureHolder.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TextureManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TextureMap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TexturePackRepository.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Textures.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TheEndPortalRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TileEntityRenderDispatcher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TileEntityRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TileRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Timer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TitleScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TntMinecartRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TntRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TrackedEntity.cpp" />
+ <ClCompile Include="..\Minecraft.Client\User.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Vertex.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VideoSettingsScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ViewportCuller.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerGolemModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerGolemRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerZombieModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WaterDropParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Iggy\gdraw\gdraw_d3d11.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\KeyboardMouseInput.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Leaderboards\WindowsLeaderboardManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\PostProcesser.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Windows64_App.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Windows64_Minecraft.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Windows64_UIController.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Network\WinsockNetLayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitchModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitchRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitherBossModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitherBossRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitherSkullRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WolfModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WolfRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WstringLookup.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Xbox\Network\NetworkPlayerXbox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ZombieModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ZombieRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\compat_shims.cpp">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="Console\ServerCli.cpp" />
+ <ClCompile Include="Console\ServerCliInput.cpp" />
+ <ClCompile Include="Console\commands\ban\CliCommandBan.cpp" />
+ <ClCompile Include="Console\commands\ban-ip\CliCommandBanIp.cpp" />
+ <ClCompile Include="Console\commands\ban-list\CliCommandBanList.cpp" />
+ <ClCompile Include="Console\commands\defaultgamemode\CliCommandDefaultGamemode.cpp" />
+ <ClCompile Include="Console\commands\enchant\CliCommandEnchant.cpp" />
+ <ClCompile Include="Console\commands\experience\CliCommandExperience.cpp" />
+ <ClCompile Include="Console\commands\gamemode\CliCommandGamemode.cpp" />
+ <ClCompile Include="Console\commands\give\CliCommandGive.cpp" />
+ <ClCompile Include="Console\commands\help\CliCommandHelp.cpp" />
+ <ClCompile Include="Console\commands\kill\CliCommandKill.cpp" />
+ <ClCompile Include="Console\commands\list\CliCommandList.cpp" />
+ <ClCompile Include="Console\commands\pardon\CliCommandPardon.cpp" />
+ <ClCompile Include="Console\commands\pardon-ip\CliCommandPardonIp.cpp" />
+ <ClCompile Include="Console\commands\stop\CliCommandStop.cpp" />
+ <ClCompile Include="Console\commands\time\CliCommandTime.cpp" />
+ <ClCompile Include="Console\commands\tp\CliCommandTp.cpp" />
+ <ClCompile Include="Console\commands\weather\CliCommandWeather.cpp" />
+ <ClCompile Include="Console\commands\whitelist\CliCommandWhitelist.cpp" />
+ <ClCompile Include="Console\ServerCliEngine.cpp" />
+ <ClCompile Include="Console\ServerCliParser.cpp" />
+ <ClCompile Include="Console\ServerCliRegistry.cpp" />
+ <ClCompile Include="Common\FileUtils.cpp" />
+ <ClCompile Include="Common\StringUtils.cpp" />
+ <ClCompile Include="..\Minecraft.Client\glWrapper.cpp" />
+ <ClCompile Include="ServerLogger.cpp" />
+ <ClCompile Include="ServerProperties.cpp" />
+ <ClCompile Include="vendor\linenoise\linenoise.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="WorldManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\stdafx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\stubs.cpp" />
+ <ClCompile Include="Windows64\ServerMain.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Access\Access.h" />
+ <ClInclude Include="Access\BanManager.h" />
+ <ClInclude Include="Access\WhitelistManager.h" />
+ <ClInclude Include="Console\ServerCli.h" />
+ <ClInclude Include="Console\ServerCliInput.h" />
+ <ClInclude Include="Console\commands\ban\CliCommandBan.h" />
+ <ClInclude Include="Console\commands\ban-ip\CliCommandBanIp.h" />
+ <ClInclude Include="Console\commands\ban-list\CliCommandBanList.h" />
+ <ClInclude Include="Console\commands\defaultgamemode\CliCommandDefaultGamemode.h" />
+ <ClInclude Include="Console\commands\enchant\CliCommandEnchant.h" />
+ <ClInclude Include="Console\commands\experience\CliCommandExperience.h" />
+ <ClInclude Include="Console\commands\gamemode\CliCommandGamemode.h" />
+ <ClInclude Include="Console\commands\give\CliCommandGive.h" />
+ <ClInclude Include="Console\commands\help\CliCommandHelp.h" />
+ <ClInclude Include="Console\commands\CommandParsing.h" />
+ <ClInclude Include="Console\commands\kill\CliCommandKill.h" />
+ <ClInclude Include="Console\commands\list\CliCommandList.h" />
+ <ClInclude Include="Console\commands\pardon\CliCommandPardon.h" />
+ <ClInclude Include="Console\commands\pardon-ip\CliCommandPardonIp.h" />
+ <ClInclude Include="Console\commands\stop\CliCommandStop.h" />
+ <ClInclude Include="Console\commands\time\CliCommandTime.h" />
+ <ClInclude Include="Console\commands\tp\CliCommandTp.h" />
+ <ClInclude Include="Console\commands\weather\CliCommandWeather.h" />
+ <ClInclude Include="Console\commands\whitelist\CliCommandWhitelist.h" />
+ <ClInclude Include="Console\commands\IServerCliCommand.h" />
+ <ClInclude Include="Console\ServerCliEngine.h" />
+ <ClInclude Include="Console\ServerCliParser.h" />
+ <ClInclude Include="Console\ServerCliRegistry.h" />
+ <ClInclude Include="Common\FileUtils.h" />
+ <ClInclude Include="Common\AccessStorageUtils.h" />
+ <ClInclude Include="Common\NetworkUtils.h" />
+ <ClInclude Include="Common\StringUtils.h" />
+ <ClInclude Include="ServerLogger.h" />
+ <ClInclude Include="ServerLogManager.h" />
+ <ClInclude Include="ServerProperties.h" />
+ <ClInclude Include="vendor\linenoise\linenoise.h" />
+ <ClInclude Include="WorldManager.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <MASM Include="..\Minecraft.Client\iob_shim.asm" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="..\Minecraft.Client\Xbox\MinecraftWindows.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Minecraft.World\Minecraft.World.vcxproj">
+ <Project>{F046C3CE-9749-4823-B32B-D9CC10B1A2C8}</Project>
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
+ </ImportGroup>
+</Project>
+
diff --git a/Minecraft.Server/Minecraft.Server.vcxproj.filters b/Minecraft.Server/Minecraft.Server.vcxproj.filters
new file mode 100644
index 00000000..8cd5e26e
--- /dev/null
+++ b/Minecraft.Server/Minecraft.Server.vcxproj.filters
@@ -0,0 +1,737 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Server">
+ <UniqueIdentifier>{A8A47C24-66C0-4912-9D34-2CBF87F1D707}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Server\Console">
+ <UniqueIdentifier>{39B037A0-9B57-454A-AF34-7D9164E22A0F}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Server\Console\Commands">
+ <UniqueIdentifier>{7C28D123-0DA3-4B17-84C0-E326F5A75740}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Server\Access">
+ <UniqueIdentifier>{29AB58D1-E8A9-465A-B3EA-BC5E9110A7A1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Server\Common">
+ <UniqueIdentifier>{BC6FD58B-1A40-45FE-B8D9-1A087C25126D}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Server\Vendor">
+ <UniqueIdentifier>{3E4D5A41-CAB8-4A10-82B5-8B2AE2E25CB2}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="ServerLogger.cpp">
+ <Filter>Server</Filter>
+ </ClCompile>
+ <ClCompile Include="ServerLogManager.cpp">
+ <Filter>Server</Filter>
+ </ClCompile>
+ <ClCompile Include="ServerProperties.cpp">
+ <Filter>Server</Filter>
+ </ClCompile>
+ <ClCompile Include="WorldManager.cpp">
+ <Filter>Server</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\ServerCli.cpp">
+ <Filter>Server\Console</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\ServerCliEngine.cpp">
+ <Filter>Server\Console</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\ServerCliParser.cpp">
+ <Filter>Server\Console</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\ServerCliRegistry.cpp">
+ <Filter>Server\Console</Filter>
+ </ClCompile>
+ <ClCompile Include="Common\StringUtils.cpp">
+ <Filter>Server\Common</Filter>
+ </ClCompile>
+ <ClCompile Include="vendor\linenoise\linenoise.c">
+ <Filter>Server\Vendor</Filter>
+ </ClCompile>
+ <ClCompile Include="Windows64\ServerMain.cpp">
+ <Filter>Server</Filter>
+ </ClCompile>
+ <ClCompile Include="Access\Access.cpp">
+ <Filter>Server\Access</Filter>
+ </ClCompile>
+ <ClCompile Include="Access\BanManager.cpp">
+ <Filter>Server\Access</Filter>
+ </ClCompile>
+ <ClCompile Include="Access\WhitelistManager.cpp">
+ <Filter>Server\Access</Filter>
+ </ClCompile>
+ <ClCompile Include="Common\FileUtils.cpp">
+ <Filter>Server\Common</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Minecraft.Client\AbstractTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\AchievementPopup.cpp" />
+ <ClCompile Include="..\Minecraft.Client\AchievementScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\AllowAllCuller.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ArchiveFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ArrowRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BatModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BatRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BeaconRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BlazeModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BlazeRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BoatModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BoatRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BookModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BossMobGuiInfo.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BreakingItemParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BubbleParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\BufferedImage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Button.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Camera.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CaveSpiderRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChatScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChestModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChestRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChickenModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ChickenRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Chunk.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ClientConnection.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ClientConstants.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ClockTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Audio\Consoles_SoundEngine.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Audio\SoundEngine.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Audio\SoundNames.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Colours\ColourTable.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\ConsoleGameMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Console_Utils.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Consoles_App.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCAudioFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCCapeFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCColourTableFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCGameRulesHeader.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCLocalisationFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCPack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCSkinFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCTextureFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\DLC\DLCUIDataFile.cpp" />
+ <ClCompile Include="..\include\lce_filesystem\lce_filesystem.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\AddEnchantmentRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\AddItemRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\ApplySchematicRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\BiomeOverride.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\CollectItemRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\CompleteAllRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\CompoundGameRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleGenerateStructure.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\ConsoleSchematicFile.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRule.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\GameRuleManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelGenerators.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRules.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\LevelRuleset.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\NamedAreaRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\StartFeature.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\UpdatePlayerRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\UseTileRuleDefinition.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionGenerateBox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceBlock.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceContainer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\GameRules\XboxStructureActionPlaceSpawner.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardInterface.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Leaderboards\LeaderboardManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Network\GameNetworkManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Network\PlatformNetworkManagerStub.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Telemetry\TelemetryManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Trial\TrialMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaConstraint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\AreaTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChangeStateConstraint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ChoiceTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\CompleteUsingItemTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ControllerTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\CraftTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\DiggerItemHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\EffectChangedTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorial.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialActiveTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\FullTutorialMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\HorseChoiceTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\InfoTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\InputConstraint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtEntityHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\LookAtTileHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\PickupTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProcedureCompoundTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\ProgressFlagTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\RideEntityTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\StatTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TakeItemHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\Tutorial.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialHint.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMessage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\TutorialTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseItemTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\UseTileTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\Tutorial\XuiCraftingTask.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AbstractContainerMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_AnvilMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BeaconMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_BrewingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CommandBlockMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_ContainerMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CraftingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_CreativeMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_DispenserMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_EnchantingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FireworksMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_FurnaceMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HUD.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HopperMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_HorseInventoryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_InventoryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_PauseMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_StartGame.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\IUIScene_TradingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIBitmapFont.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Chat.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIConsole.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_DebugUIMarketingGuide.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Logo.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_MenuBackground.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Panorama.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_PressStartToPlay.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_Tooltips.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIComponent_TutorialPopup.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Base.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BeaconEffectButton.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_BitmapIcon.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Button.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_ButtonList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_CheckBox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Cursor.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DLCList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_DynamicLabel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentBook.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_EnchantmentButton.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_HTMLLabel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Label.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_LeaderboardList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftHorse.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_MinecraftPlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_PlayerSkinPreview.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Progress.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SaveList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_Slider.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SlotList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_SpaceIndicatorBar.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TextInput.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIControl_TexturePackList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIController.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIFontData.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIGroup.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UILayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AbstractContainerMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_AnvilMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BeaconMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_BrewingStandMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ConnectingProgress.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ContainerMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ControlsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CraftingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreateWorldMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_CreativeMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Credits.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCMainMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DLCOffersMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DeathMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugCreateSchematic.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOptions.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugOverlay.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DebugSetCamera.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_DispenserMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EULA.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EnchantingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_EndPoem.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FireworksMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FullscreenProgress.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_FurnaceMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HUD.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HelpAndOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HopperMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HorseInventoryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlay.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_HowToPlayMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameHostOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGameInfoMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InGamePlayerOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Intro.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_InventoryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_JoinMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Keyboard.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LanguageSelector.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LaunchMoreOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LeaderboardsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_LoadOrJoinMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MainMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_MessageBox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_NewUpdateMessage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_PauseMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_QuadrantSignin.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_ReinstallMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SaveMessage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsAudioMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsControlMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsGraphicsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsOptionsMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SettingsUIMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SignEntryMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_SkinSelectMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TeleportMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_Timer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TradingMenu.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIScene_TrialExitUpsell.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UIString.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\UI\UITTFFont.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\adler32.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\compress.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\crc32.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\deflate.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\gzclose.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\gzlib.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\gzread.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\gzwrite.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\infback.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\inffast.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\inflate.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\inftrees.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\trees.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\uncompr.c" />
+ <ClCompile Include="..\Minecraft.Client\Common\zlib\zutil.c" />
+ <ClCompile Include="..\Minecraft.Client\CompassTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ConfirmScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ConsoleInput.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ControlsScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CowModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CowRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CreateWorldScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CreeperModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CreeperRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CritParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\CritParticle2.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Cube.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DLCTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DeathScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DefaultRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DefaultTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DemoUser.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DerivedServerLevel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DirtyChunkSorter.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DispenserBootstrap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DistanceChunkSorter.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DragonBreathParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DragonModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\DripParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EchantmentTableParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EditBox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnchantTableRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderChestRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderCrystalModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderCrystalRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderDragonRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EnderParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EndermanModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EndermanRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EntityRenderDispatcher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EntityRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EntityTileRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\EntityTracker.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ErrorScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ExperienceOrbRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ExplodeParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Extrax64Stubs.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FallingTileRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FileTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FireballRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FireworksParticles.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FishingHookRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FlameParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FolderTexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Font.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FootstepParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Frustum.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FrustumCuller.cpp" />
+ <ClCompile Include="..\Minecraft.Client\FrustumData.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GameRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GhastModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GhastRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GiantMobRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Gui.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GuiComponent.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GuiMessage.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GuiParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\GuiParticles.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HeartParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HorseRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HttpTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HugeExplosionParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HugeExplosionSeedParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HumanoidMobRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\HumanoidModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\InBedChatScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Input.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ItemFrameRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ItemInHandRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ItemRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ItemSpriteRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\JoinMultiplayerScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\KeyMapping.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LargeChestModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LavaParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LavaSlimeModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LavaSlimeRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LeashKnotModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LeashKnotRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LevelRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Lighting.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LightningBoltRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LivingEntityRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\LocalPlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MemTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MemoryTracker.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MinecartModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MinecartRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MinecartSpawnerRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Minecraft.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MinecraftServer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Minimap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MobRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MobSkinMemTextureProcessor.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MobSkinTextureProcessor.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MobSpawnerRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Model.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ModelHorse.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ModelPart.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MultiPlayerChunkCache.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MultiPlayerGameMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MultiPlayerLevel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MultiPlayerLocalPlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\MushroomCowRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\NameEntryScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\NetherPortalParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\NoteParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\OcelotModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\OcelotRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\OffsettedRenderList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Options.cpp" />
+ <ClCompile Include="..\Minecraft.Client\OptionsScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PaintingRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Particle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ParticleEngine.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PauseScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PendingConnection.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PigModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PigRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PistonPieceRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerChunkMap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerCloudParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerConnection.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PlayerRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Polygon.cpp" />
+ <ClCompile Include="..\Minecraft.Client\PreStitchedTextureMap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ProgressRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\QuadrupedModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Rect2i.cpp" />
+ <ClCompile Include="..\Minecraft.Client\RedDustParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\RemotePlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\RenameWorldScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Screen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ScreenSizeCalculator.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ScrolledSelectionList.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SelectWorldScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerChunkCache.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerCommandDispatcher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerConnection.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerLevel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerLevelListener.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerPlayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerPlayerGameMode.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ServerScoreboard.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Settings.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SheepFurModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SheepModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SheepRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SignModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SignRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SilverfishModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SilverfishRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SimpleIcon.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkeletonHeadModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkeletonModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkeletonRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkiModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SkullTileRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SlideButton.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SlimeModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SlimeRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SmallButton.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SmokeParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SnowManModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SnowManRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SnowShovelParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SpellParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SpiderModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SpiderRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SplashParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SquidModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SquidRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StatsCounter.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StatsScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StatsSyncher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StitchSlot.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StitchedTexture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Stitcher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\StringTable.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SuspendedParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\SuspendedTownParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TakeAnimationParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TeleportCommand.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TerrainParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Tesselator.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TexOffs.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Texture.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TextureAtlas.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TextureHolder.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TextureManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TextureMap.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TexturePack.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TexturePackRepository.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Textures.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TheEndPortalRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TileEntityRenderDispatcher.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TileEntityRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TileRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Timer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TitleScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TntMinecartRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TntRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\TrackedEntity.cpp" />
+ <ClCompile Include="..\Minecraft.Client\User.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Vertex.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VideoSettingsScreen.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ViewportCuller.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerGolemModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerGolemRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\VillagerZombieModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WaterDropParticle.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Iggy\gdraw\gdraw_d3d11.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\KeyboardMouseInput.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Leaderboards\WindowsLeaderboardManager.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\PostProcesser.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Windows64_App.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Windows64_Minecraft.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Windows64_UIController.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Windows64\Network\WinsockNetLayer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitchModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitchRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitherBossModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitherBossRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WitherSkullRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WolfModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WolfRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\WstringLookup.cpp" />
+ <ClCompile Include="..\Minecraft.Client\Xbox\Network\NetworkPlayerXbox.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ZombieModel.cpp" />
+ <ClCompile Include="..\Minecraft.Client\ZombieRenderer.cpp" />
+ <ClCompile Include="..\Minecraft.Client\compat_shims.cpp" />
+ <ClCompile Include="Console\ServerCliInput.cpp" />
+ <ClCompile Include="..\Minecraft.Client\glWrapper.cpp" />
+ <ClCompile Include="..\Minecraft.Client\stdafx.cpp" />
+ <ClCompile Include="..\Minecraft.Client\stubs.cpp" />
+ <ClCompile Include="Console\commands\ban\CliCommandBan.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\ban-ip\CliCommandBanIp.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\ban-list\CliCommandBanList.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\defaultgamemode\CliCommandDefaultGamemode.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\enchant\CliCommandEnchant.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\experience\CliCommandExperience.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\gamemode\CliCommandGamemode.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\give\CliCommandGive.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\help\CliCommandHelp.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\kill\CliCommandKill.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\list\CliCommandList.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\pardon\CliCommandPardon.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\pardon-ip\CliCommandPardonIp.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\stop\CliCommandStop.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\time\CliCommandTime.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\tp\CliCommandTp.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\weather\CliCommandWeather.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ <ClCompile Include="Console\commands\whitelist\CliCommandWhitelist.cpp">
+ <Filter>Server\Console\Commands</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Console\ServerCli.h">
+ <Filter>Server\Console</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\ServerCliEngine.h">
+ <Filter>Server\Console</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\ServerCliParser.h">
+ <Filter>Server\Console</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\ServerCliRegistry.h">
+ <Filter>Server\Console</Filter>
+ </ClInclude>
+ <ClInclude Include="Access\Access.h">
+ <Filter>Server\Access</Filter>
+ </ClInclude>
+ <ClInclude Include="Access\BanManager.h">
+ <Filter>Server\Access</Filter>
+ </ClInclude>
+ <ClInclude Include="Access\WhitelistManager.h">
+ <Filter>Server\Access</Filter>
+ </ClInclude>
+ <ClInclude Include="Common\FileUtils.h">
+ <Filter>Server\Common</Filter>
+ </ClInclude>
+ <ClInclude Include="Common\AccessStorageUtils.h">
+ <Filter>Server\Common</Filter>
+ </ClInclude>
+ <ClInclude Include="Common\NetworkUtils.h">
+ <Filter>Server\Common</Filter>
+ </ClInclude>
+ <ClInclude Include="Common\StringUtils.h">
+ <Filter>Server\Common</Filter>
+ </ClInclude>
+ <ClInclude Include="ServerLogger.h">
+ <Filter>Server</Filter>
+ </ClInclude>
+ <ClInclude Include="ServerLogManager.h">
+ <Filter>Server</Filter>
+ </ClInclude>
+ <ClInclude Include="ServerProperties.h">
+ <Filter>Server</Filter>
+ </ClInclude>
+ <ClInclude Include="vendor\linenoise\linenoise.h">
+ <Filter>Server\Vendor</Filter>
+ </ClInclude>
+ <ClInclude Include="WorldManager.h">
+ <Filter>Server</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\ServerCliInput.h" />
+ <ClInclude Include="Console\commands\ban\CliCommandBan.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\ban-ip\CliCommandBanIp.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\ban-list\CliCommandBanList.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\defaultgamemode\CliCommandDefaultGamemode.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\enchant\CliCommandEnchant.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\experience\CliCommandExperience.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\gamemode\CliCommandGamemode.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\give\CliCommandGive.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\help\CliCommandHelp.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\CommandParsing.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\kill\CliCommandKill.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\list\CliCommandList.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\pardon\CliCommandPardon.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\pardon-ip\CliCommandPardonIp.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\stop\CliCommandStop.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\time\CliCommandTime.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\tp\CliCommandTp.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\weather\CliCommandWeather.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\whitelist\CliCommandWhitelist.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ <ClInclude Include="Console\commands\IServerCliCommand.h">
+ <Filter>Server\Console\Commands</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="..\Minecraft.Client\Xbox\MinecraftWindows.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <MASM Include="..\Minecraft.Client\iob_shim.asm" />
+ </ItemGroup>
+</Project>
+
diff --git a/Minecraft.Server/ServerLogManager.cpp b/Minecraft.Server/ServerLogManager.cpp
new file mode 100644
index 00000000..84805f7e
--- /dev/null
+++ b/Minecraft.Server/ServerLogManager.cpp
@@ -0,0 +1,402 @@
+#include "stdafx.h"
+
+#include "ServerLogManager.h"
+
+#include "Common\StringUtils.h"
+#include "ServerLogger.h"
+
+#include <array>
+#include <mutex>
+
+extern bool g_Win64DedicatedServer;
+
+namespace ServerRuntime
+{
+ namespace ServerLogManager
+ {
+ namespace
+ {
+ /**
+ * **!! This information is managed solely for logging purposes, but it is questionable from a liability perspective, so it will eventually need to be separated !!**
+ *
+ * Tracks the remote IP and accepted player name associated with one `smallId`
+ * 1つのsmallIdに紐づく接続IPとプレイヤー名を保持する
+ */
+ struct ConnectionLogEntry
+ {
+ std::string remoteIp;
+ std::string playerName;
+ };
+
+ /**
+ * Owns the shared connection cache used by hook points running on different threads
+ * 複数スレッドのhookから共有される接続キャッシュを保持する
+ */
+ struct ServerLogState
+ {
+ std::mutex stateLock;
+ std::array<ConnectionLogEntry, 256> entries;
+ };
+
+ ServerLogState g_serverLogState;
+
+ static bool IsDedicatedServerLoggingEnabled()
+ {
+ return g_Win64DedicatedServer;
+ }
+
+ static void ResetConnectionLogEntry(ConnectionLogEntry *entry)
+ {
+ if (entry == NULL)
+ {
+ return;
+ }
+
+ entry->remoteIp.clear();
+ entry->playerName.clear();
+ }
+
+ static std::string NormalizeRemoteIp(const char *ip)
+ {
+ if (ip == NULL || ip[0] == 0)
+ {
+ return std::string("unknown");
+ }
+
+ return std::string(ip);
+ }
+
+ static std::string NormalizePlayerName(const std::wstring &playerName)
+ {
+ std::string playerNameUtf8 = StringUtils::WideToUtf8(playerName);
+ if (playerNameUtf8.empty())
+ {
+ playerNameUtf8 = "<unknown>";
+ }
+
+ return playerNameUtf8;
+ }
+
+ // Default to the main app channel when the caller does not provide a source tag.
+ static const char *NormalizeClientLogSource(const char *source)
+ {
+ if (source == NULL || source[0] == 0)
+ {
+ return "app";
+ }
+
+ return source;
+ }
+
+ static void EmitClientDebugLogLine(const char *source, const std::string &line)
+ {
+ if (line.empty())
+ {
+ return;
+ }
+
+ LogDebugf("client", "[%s] %s", NormalizeClientLogSource(source), line.c_str());
+ }
+
+ // Split one debug payload into individual lines so each line becomes a prompt-safe server log entry.
+ static void ForwardClientDebugMessage(const char *source, const char *message)
+ {
+ if (message == NULL || message[0] == 0)
+ {
+ return;
+ }
+
+ const char *cursor = message;
+ while (*cursor != 0)
+ {
+ const char *lineStart = cursor;
+ while (*cursor != 0 && *cursor != '\r' && *cursor != '\n')
+ {
+ ++cursor;
+ }
+
+ // Split multi-line client debug output into prompt-safe server log entries.
+ if (cursor > lineStart)
+ {
+ EmitClientDebugLogLine(source, std::string(lineStart, (size_t)(cursor - lineStart)));
+ }
+
+ while (*cursor == '\r' || *cursor == '\n')
+ {
+ ++cursor;
+ }
+ }
+ }
+
+ // Share the same formatting path for app, user, and legacy debug-spew forwards.
+ static void ForwardFormattedClientDebugLogV(const char *source, const char *format, va_list args)
+ {
+ if (!IsDedicatedServerLoggingEnabled() || format == NULL || format[0] == 0)
+ {
+ return;
+ }
+
+ char messageBuffer[2048] = {};
+ vsnprintf_s(messageBuffer, sizeof(messageBuffer), _TRUNCATE, format, args);
+ ForwardClientDebugMessage(source, messageBuffer);
+ }
+
+ static const char *TcpRejectReasonToString(ETcpRejectReason reason)
+ {
+ switch (reason)
+ {
+ case eTcpRejectReason_BannedIp: return "banned-ip";
+ case eTcpRejectReason_GameNotReady: return "game-not-ready";
+ case eTcpRejectReason_ServerFull: return "server-full";
+ default: return "unknown";
+ }
+ }
+
+ static const char *LoginRejectReasonToString(ELoginRejectReason reason)
+ {
+ switch (reason)
+ {
+ case eLoginRejectReason_BannedXuid: return "banned-xuid";
+ case eLoginRejectReason_NotWhitelisted: return "not-whitelisted";
+ case eLoginRejectReason_DuplicateXuid: return "duplicate-xuid";
+ case eLoginRejectReason_DuplicateName: return "duplicate-name";
+ default: return "unknown";
+ }
+ }
+
+ static const char *DisconnectReasonToString(DisconnectPacket::eDisconnectReason reason)
+ {
+ switch (reason)
+ {
+ case DisconnectPacket::eDisconnect_None: return "none";
+ case DisconnectPacket::eDisconnect_Quitting: return "quitting";
+ case DisconnectPacket::eDisconnect_Closed: return "closed";
+ case DisconnectPacket::eDisconnect_LoginTooLong: return "login-too-long";
+ case DisconnectPacket::eDisconnect_IllegalStance: return "illegal-stance";
+ case DisconnectPacket::eDisconnect_IllegalPosition: return "illegal-position";
+ case DisconnectPacket::eDisconnect_MovedTooQuickly: return "moved-too-quickly";
+ case DisconnectPacket::eDisconnect_NoFlying: return "no-flying";
+ case DisconnectPacket::eDisconnect_Kicked: return "kicked";
+ case DisconnectPacket::eDisconnect_TimeOut: return "timeout";
+ case DisconnectPacket::eDisconnect_Overflow: return "overflow";
+ case DisconnectPacket::eDisconnect_EndOfStream: return "end-of-stream";
+ case DisconnectPacket::eDisconnect_ServerFull: return "server-full";
+ case DisconnectPacket::eDisconnect_OutdatedServer: return "outdated-server";
+ case DisconnectPacket::eDisconnect_OutdatedClient: return "outdated-client";
+ case DisconnectPacket::eDisconnect_UnexpectedPacket: return "unexpected-packet";
+ case DisconnectPacket::eDisconnect_ConnectionCreationFailed: return "connection-creation-failed";
+ case DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesHost: return "no-multiplayer-privileges-host";
+ case DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesJoin: return "no-multiplayer-privileges-join";
+ case DisconnectPacket::eDisconnect_NoUGC_AllLocal: return "no-ugc-all-local";
+ case DisconnectPacket::eDisconnect_NoUGC_Single_Local: return "no-ugc-single-local";
+ case DisconnectPacket::eDisconnect_ContentRestricted_AllLocal: return "content-restricted-all-local";
+ case DisconnectPacket::eDisconnect_ContentRestricted_Single_Local: return "content-restricted-single-local";
+ case DisconnectPacket::eDisconnect_NoUGC_Remote: return "no-ugc-remote";
+ case DisconnectPacket::eDisconnect_NoFriendsInGame: return "no-friends-in-game";
+ case DisconnectPacket::eDisconnect_Banned: return "banned";
+ case DisconnectPacket::eDisconnect_NotFriendsWithHost: return "not-friends-with-host";
+ case DisconnectPacket::eDisconnect_NATMismatch: return "nat-mismatch";
+ default: return "unknown";
+ }
+ }
+ }
+
+ // Only forward client-side debug output while the process is running as the dedicated server.
+ bool ShouldForwardClientDebugLogs()
+ {
+ return IsDedicatedServerLoggingEnabled();
+ }
+
+ void ForwardClientAppDebugLogV(const char *format, va_list args)
+ {
+ ForwardFormattedClientDebugLogV("app", format, args);
+ }
+
+ void ForwardClientUserDebugLogV(int user, const char *format, va_list args)
+ {
+ char source[32] = {};
+ _snprintf_s(source, sizeof(source), _TRUNCATE, "app:user=%d", user);
+ ForwardFormattedClientDebugLogV(source, format, args);
+ }
+
+ void ForwardClientDebugSpewLogV(const char *format, va_list args)
+ {
+ ForwardFormattedClientDebugLogV("debug-spew", format, args);
+ }
+
+ // Clear every cached connection slot during startup so stale metadata never leaks into future logs.
+ void Initialize()
+ {
+ std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
+ for (size_t index = 0; index < g_serverLogState.entries.size(); ++index)
+ {
+ ResetConnectionLogEntry(&g_serverLogState.entries[index]);
+ }
+ }
+
+ // Reuse Initialize as the shutdown cleanup path because both operations wipe the cache.
+ void Shutdown()
+ {
+ Initialize();
+ }
+
+ // Log the raw socket arrival before a smallId is assigned so early rejects still have an IP in the logs.
+ void OnIncomingTcpConnection(const char *ip)
+ {
+ if (!IsDedicatedServerLoggingEnabled())
+ {
+ return;
+ }
+
+ const std::string remoteIp = NormalizeRemoteIp(ip);
+ LogInfof("network", "incoming tcp connection from %s", remoteIp.c_str());
+ }
+
+ // TCP rejects happen before connection state is cached, so log directly from the supplied remote IP.
+ void OnRejectedTcpConnection(const char *ip, ETcpRejectReason reason)
+ {
+ if (!IsDedicatedServerLoggingEnabled())
+ {
+ return;
+ }
+
+ const std::string remoteIp = NormalizeRemoteIp(ip);
+ LogWarnf("network", "rejected tcp connection from %s: reason=%s", remoteIp.c_str(), TcpRejectReasonToString(reason));
+ }
+
+ // Cache the accepted remote IP immediately so later login and disconnect logs can reuse it.
+ void OnAcceptedTcpConnection(unsigned char smallId, const char *ip)
+ {
+ if (!IsDedicatedServerLoggingEnabled())
+ {
+ return;
+ }
+
+ const std::string remoteIp = NormalizeRemoteIp(ip);
+ {
+ std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
+ ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
+ ResetConnectionLogEntry(&entry);
+ entry.remoteIp = remoteIp;
+ }
+
+ LogInfof("network", "accepted tcp connection from %s as smallId=%u", remoteIp.c_str(), (unsigned)smallId);
+ }
+
+ // Once login succeeds, bind the resolved player name onto the cached transport entry.
+ void OnAcceptedPlayerLogin(unsigned char smallId, const std::wstring &playerName)
+ {
+ if (!IsDedicatedServerLoggingEnabled())
+ {
+ return;
+ }
+
+ const std::string playerNameUtf8 = NormalizePlayerName(playerName);
+ std::string remoteIp("unknown");
+ {
+ std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
+ ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
+ entry.playerName = playerNameUtf8;
+ if (!entry.remoteIp.empty())
+ {
+ remoteIp = entry.remoteIp;
+ }
+ }
+
+ LogInfof("network", "accepted player login: name=\"%s\" ip=%s smallId=%u", playerNameUtf8.c_str(), remoteIp.c_str(), (unsigned)smallId);
+ }
+
+ // Read the cached IP for the rejection log, then clear the slot because the player never fully joined.
+ void OnRejectedPlayerLogin(unsigned char smallId, const std::wstring &playerName, ELoginRejectReason reason)
+ {
+ if (!IsDedicatedServerLoggingEnabled())
+ {
+ return;
+ }
+
+ const std::string playerNameUtf8 = NormalizePlayerName(playerName);
+ std::string remoteIp("unknown");
+ {
+ std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
+ ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
+ if (!entry.remoteIp.empty())
+ {
+ remoteIp = entry.remoteIp;
+ }
+ ResetConnectionLogEntry(&entry);
+ }
+
+ LogWarnf("network", "rejected login from %s: name=\"%s\" reason=%s", remoteIp.c_str(), playerNameUtf8.c_str(), LoginRejectReasonToString(reason));
+ }
+
+ // Disconnect logging is the final consumer of cached metadata, so it also clears the slot afterward.
+ void OnPlayerDisconnected(
+ unsigned char smallId,
+ const std::wstring &playerName,
+ DisconnectPacket::eDisconnectReason reason,
+ bool initiatedByServer)
+ {
+ if (!IsDedicatedServerLoggingEnabled())
+ {
+ return;
+ }
+
+ std::string playerNameUtf8 = NormalizePlayerName(playerName);
+ std::string remoteIp("unknown");
+ {
+ // Copy state under lock and emit the log after unlocking so CLI output never blocks connection bookkeeping.
+ std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
+ ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
+ if (!entry.remoteIp.empty())
+ {
+ remoteIp = entry.remoteIp;
+ }
+ if (playerNameUtf8 == "<unknown>" && !entry.playerName.empty())
+ {
+ playerNameUtf8 = entry.playerName;
+ }
+ ResetConnectionLogEntry(&entry);
+ }
+
+ LogInfof(
+ "network",
+ "%s: name=\"%s\" ip=%s smallId=%u reason=%s",
+ initiatedByServer ? "disconnecting player" : "player disconnected",
+ playerNameUtf8.c_str(),
+ remoteIp.c_str(),
+ (unsigned)smallId,
+ DisconnectReasonToString(reason));
+ }
+
+ /**
+ * For logging purposes, the responsibility is technically misplaced, but the IP is cached in `LogManager`.
+ * Those cached values are then used to retrieve the player's IP.
+ *
+ * Eventually, this should be implemented in a separate class or on the `Minecraft.Client` side instead.
+ */
+ bool TryGetConnectionRemoteIp(unsigned char smallId, std::string *outIp)
+ {
+ if (!IsDedicatedServerLoggingEnabled() || outIp == NULL)
+ {
+ return false;
+ }
+
+ std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
+ const ConnectionLogEntry &entry = g_serverLogState.entries[smallId];
+ if (entry.remoteIp.empty() || entry.remoteIp == "unknown")
+ {
+ return false;
+ }
+
+ *outIp = entry.remoteIp;
+ return true;
+ }
+
+ // Provide explicit cache cleanup for paths that terminate without going through disconnect logging.
+ void ClearConnection(unsigned char smallId)
+ {
+ std::lock_guard<std::mutex> stateLock(g_serverLogState.stateLock);
+ ResetConnectionLogEntry(&g_serverLogState.entries[smallId]);
+ }
+ }
+}
diff --git a/Minecraft.Server/ServerLogManager.h b/Minecraft.Server/ServerLogManager.h
new file mode 100644
index 00000000..1d4abfb5
--- /dev/null
+++ b/Minecraft.Server/ServerLogManager.h
@@ -0,0 +1,127 @@
+#pragma once
+
+#include <string>
+#include <stdarg.h>
+
+#include "..\Minecraft.World\DisconnectPacket.h"
+
+namespace ServerRuntime
+{
+ namespace ServerLogManager
+ {
+ /**
+ * Identifies why the dedicated server rejected a TCP connection before login completed
+ * ログイン完了前にTCP接続を拒否した理由
+ */
+ enum ETcpRejectReason
+ {
+ eTcpRejectReason_BannedIp = 0,
+ eTcpRejectReason_GameNotReady,
+ eTcpRejectReason_ServerFull
+ };
+
+ /**
+ * Identifies why the dedicated server rejected a player during login validation
+ * ログイン検証中にプレイヤーを拒否した理由
+ */
+ enum ELoginRejectReason
+ {
+ eLoginRejectReason_BannedXuid = 0,
+ eLoginRejectReason_NotWhitelisted,
+ eLoginRejectReason_DuplicateXuid,
+ eLoginRejectReason_DuplicateName
+ };
+
+ /**
+ * Returns `true` when client-side debug logs should be redirected into the dedicated server logger
+ * dedicated server時にclient側デバッグログを転送すかどうか
+ */
+ bool ShouldForwardClientDebugLogs();
+
+ /**
+ * Formats and forwards `CMinecraftApp::DebugPrintf` output through the dedicated server logger
+ * CMinecraftApp::DebugPrintf の出力を専用サーバーロガーへ転送
+ */
+ void ForwardClientAppDebugLogV(const char *format, va_list args);
+
+ /**
+ * Formats and forwards `CMinecraftApp::DebugPrintf(int user, ...)` output through the dedicated server logger
+ * CMinecraftApp::DebugPrintf(int user, ...) の出力を専用サーバーロガーへ転送
+ */
+ void ForwardClientUserDebugLogV(int user, const char *format, va_list args);
+
+ /**
+ * Formats and forwards legacy `DebugSpew` output through the dedicated server logger
+ * 従来の DebugSpew 出力を専用サーバーロガーへ転送
+ */
+ void ForwardClientDebugSpewLogV(const char *format, va_list args);
+
+ /**
+ * Clears cached connection metadata before the dedicated server starts accepting players
+ * 接続ログ管理用のキャッシュを初期化
+ */
+ void Initialize();
+
+ /**
+ * Releases cached connection metadata after the dedicated server stops
+ * 接続ログ管理用のキャッシュを停止時に破棄
+ */
+ void Shutdown();
+
+ /**
+ * **Log Incoming TCP Connection**
+ *
+ * Emits a named log for a raw TCP accept before smallId assignment finishes
+ * smallId割り当て前のTCP接続を記録
+ */
+ void OnIncomingTcpConnection(const char *ip);
+
+ /**
+ * Emits a named log for a TCP connection rejected before login starts
+ * ログイン開始前に拒否したTCP接続を記録
+ */
+ void OnRejectedTcpConnection(const char *ip, ETcpRejectReason reason);
+
+ /**
+ * Stores the remote IP for the assigned smallId and logs the accepted transport connection
+ * 割り当て済みsmallIdに対接続IPを保存して記録
+ */
+ void OnAcceptedTcpConnection(unsigned char smallId, const char *ip);
+
+ /**
+ * Associates a player name with the connection and emits the accepted login log
+ * 接続にプレイヤー名を関連付けてログイン成功を記録
+ */
+ void OnAcceptedPlayerLogin(unsigned char smallId, const std::wstring &playerName);
+
+ /**
+ * Emits a named login rejection log and clears cached metadata for that smallId
+ * ログイン拒否を記録し対象smallIdのキャッシュを破棄
+ */
+ void OnRejectedPlayerLogin(unsigned char smallId, const std::wstring &playerName, ELoginRejectReason reason);
+
+ /**
+ * Emits a named disconnect log using cached connection metadata and then clears that entry
+ * 接続キャッシュを使って切断ログを出しその後で破棄
+ */
+ void OnPlayerDisconnected(
+ unsigned char smallId,
+ const std::wstring &playerName,
+ DisconnectPacket::eDisconnectReason reason,
+ bool initiatedByServer);
+
+ /**
+ * Reads the cached remote IP for a live smallId without consuming the entry
+ * Eventually, this should be implemented in a separate class or on the `Minecraft.Client` side instead.
+ *
+ * 指定smallIdの接続IPをキャッシュから参照する
+ */
+ bool TryGetConnectionRemoteIp(unsigned char smallId, std::string *outIp);
+
+ /**
+ * Removes any remembered IP or player name for the specified smallId
+ * 指定smallIdに紐づく接続キャッシュを消去
+ */
+ void ClearConnection(unsigned char smallId);
+ }
+}
diff --git a/Minecraft.Server/ServerLogger.cpp b/Minecraft.Server/ServerLogger.cpp
new file mode 100644
index 00000000..0c7c567f
--- /dev/null
+++ b/Minecraft.Server/ServerLogger.cpp
@@ -0,0 +1,258 @@
+#include "stdafx.h"
+
+#include "ServerLogger.h"
+#include "Common\\StringUtils.h"
+#include "vendor\\linenoise\\linenoise.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+namespace ServerRuntime
+{
+static volatile LONG g_minLogLevel = (LONG)eServerLogLevel_Info;
+
+static const char *NormalizeCategory(const char *category)
+{
+ if (category == NULL || category[0] == 0)
+ {
+ return "server";
+ }
+ return category;
+}
+
+static const char *LogLevelToString(EServerLogLevel level)
+{
+ switch (level)
+ {
+ case eServerLogLevel_Debug:
+ return "DEBUG";
+ case eServerLogLevel_Info:
+ return "INFO";
+ case eServerLogLevel_Warn:
+ return "WARN";
+ case eServerLogLevel_Error:
+ return "ERROR";
+ default:
+ return "INFO";
+ }
+}
+
+static WORD LogLevelToColor(EServerLogLevel level)
+{
+ switch (level)
+ {
+ case eServerLogLevel_Debug:
+ return FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
+ case eServerLogLevel_Warn:
+ return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ case eServerLogLevel_Error:
+ return FOREGROUND_RED | FOREGROUND_INTENSITY;
+ case eServerLogLevel_Info:
+ default:
+ return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
+ }
+}
+
+static void BuildTimestamp(char *buffer, size_t bufferSize)
+{
+ if (buffer == NULL || bufferSize == 0)
+ {
+ return;
+ }
+
+ SYSTEMTIME localTime;
+ GetLocalTime(&localTime);
+ sprintf_s(
+ buffer,
+ bufferSize,
+ "%04u-%02u-%02u %02u:%02u:%02u.%03u",
+ (unsigned)localTime.wYear,
+ (unsigned)localTime.wMonth,
+ (unsigned)localTime.wDay,
+ (unsigned)localTime.wHour,
+ (unsigned)localTime.wMinute,
+ (unsigned)localTime.wSecond,
+ (unsigned)localTime.wMilliseconds);
+}
+
+static bool ShouldLog(EServerLogLevel level)
+{
+ return ((LONG)level >= g_minLogLevel);
+}
+
+static void WriteLogLine(EServerLogLevel level, const char *category, const char *message)
+{
+ if (!ShouldLog(level))
+ {
+ return;
+ }
+
+ linenoiseExternalWriteBegin();
+
+ const char *safeCategory = NormalizeCategory(category);
+ const char *safeMessage = (message != NULL) ? message : "";
+
+ char timestamp[32] = {};
+ BuildTimestamp(timestamp, sizeof(timestamp));
+
+ HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+ CONSOLE_SCREEN_BUFFER_INFO originalInfo;
+ bool hasColorConsole = false;
+ if (stdoutHandle != INVALID_HANDLE_VALUE && stdoutHandle != NULL)
+ {
+ if (GetConsoleScreenBufferInfo(stdoutHandle, &originalInfo))
+ {
+ hasColorConsole = true;
+ SetConsoleTextAttribute(stdoutHandle, LogLevelToColor(level));
+ }
+ }
+
+ printf(
+ "[%s][%s][%s] %s\n",
+ timestamp,
+ LogLevelToString(level),
+ safeCategory,
+ safeMessage);
+ fflush(stdout);
+
+ if (hasColorConsole)
+ {
+ SetConsoleTextAttribute(stdoutHandle, originalInfo.wAttributes);
+ }
+
+ linenoiseExternalWriteEnd();
+}
+
+static void WriteLogLineV(EServerLogLevel level, const char *category, const char *format, va_list args)
+{
+ char messageBuffer[2048] = {};
+ if (format == NULL)
+ {
+ WriteLogLine(level, category, "");
+ return;
+ }
+
+ vsnprintf_s(messageBuffer, sizeof(messageBuffer), _TRUNCATE, format, args);
+ WriteLogLine(level, category, messageBuffer);
+}
+
+bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel)
+{
+ if (value == NULL || outLevel == NULL)
+ {
+ return false;
+ }
+
+ if (_stricmp(value, "debug") == 0)
+ {
+ *outLevel = eServerLogLevel_Debug;
+ return true;
+ }
+ if (_stricmp(value, "info") == 0)
+ {
+ *outLevel = eServerLogLevel_Info;
+ return true;
+ }
+ if (_stricmp(value, "warn") == 0 || _stricmp(value, "warning") == 0)
+ {
+ *outLevel = eServerLogLevel_Warn;
+ return true;
+ }
+ if (_stricmp(value, "error") == 0)
+ {
+ *outLevel = eServerLogLevel_Error;
+ return true;
+ }
+
+ return false;
+}
+
+void SetServerLogLevel(EServerLogLevel level)
+{
+ if (level < eServerLogLevel_Debug)
+ {
+ level = eServerLogLevel_Debug;
+ }
+ else if (level > eServerLogLevel_Error)
+ {
+ level = eServerLogLevel_Error;
+ }
+
+ g_minLogLevel = (LONG)level;
+}
+
+EServerLogLevel GetServerLogLevel()
+{
+ return (EServerLogLevel)g_minLogLevel;
+}
+
+void LogDebug(const char *category, const char *message)
+{
+ WriteLogLine(eServerLogLevel_Debug, category, message);
+}
+
+void LogInfo(const char *category, const char *message)
+{
+ WriteLogLine(eServerLogLevel_Info, category, message);
+}
+
+void LogWarn(const char *category, const char *message)
+{
+ WriteLogLine(eServerLogLevel_Warn, category, message);
+}
+
+void LogError(const char *category, const char *message)
+{
+ WriteLogLine(eServerLogLevel_Error, category, message);
+}
+
+void LogDebugf(const char *category, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ WriteLogLineV(eServerLogLevel_Debug, category, format, args);
+ va_end(args);
+}
+
+void LogInfof(const char *category, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ WriteLogLineV(eServerLogLevel_Info, category, format, args);
+ va_end(args);
+}
+
+void LogWarnf(const char *category, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ WriteLogLineV(eServerLogLevel_Warn, category, format, args);
+ va_end(args);
+}
+
+void LogErrorf(const char *category, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ WriteLogLineV(eServerLogLevel_Error, category, format, args);
+ va_end(args);
+}
+
+void LogStartupStep(const char *message)
+{
+ LogInfo("startup", message);
+}
+
+void LogWorldIO(const char *message)
+{
+ LogInfo("world-io", message);
+}
+
+void LogWorldName(const char *prefix, const std::wstring &name)
+{
+ std::string utf8 = StringUtils::WideToUtf8(name);
+ LogInfof("world-io", "%s: %s", (prefix != NULL) ? prefix : "name", utf8.c_str());
+}
+}
+
diff --git a/Minecraft.Server/ServerLogger.h b/Minecraft.Server/ServerLogger.h
new file mode 100644
index 00000000..89b820e6
--- /dev/null
+++ b/Minecraft.Server/ServerLogger.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <string>
+
+namespace ServerRuntime
+{
+ enum EServerLogLevel
+ {
+ eServerLogLevel_Debug = 0,
+ eServerLogLevel_Info = 1,
+ eServerLogLevel_Warn = 2,
+ eServerLogLevel_Error = 3
+ };
+
+ /**
+ * **Parse Log Level String**
+ *
+ * Converts a string value into log level (`debug`/`info`/`warn`/`error`)
+ * ログレベル文字列の変換処理
+ *
+ * @param value Source string
+ * @param outLevel Output location for parsed level
+ * @return `true` when conversion succeeds
+ */
+ bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel);
+
+ void SetServerLogLevel(EServerLogLevel level);
+ EServerLogLevel GetServerLogLevel();
+
+ void LogDebug(const char *category, const char *message);
+ void LogInfo(const char *category, const char *message);
+ void LogWarn(const char *category, const char *message);
+ void LogError(const char *category, const char *message);
+
+ /** Emit formatted log output with the specified level and category */
+ void LogDebugf(const char *category, const char *format, ...);
+ void LogInfof(const char *category, const char *format, ...);
+ void LogWarnf(const char *category, const char *format, ...);
+ void LogErrorf(const char *category, const char *format, ...);
+
+ void LogStartupStep(const char *message);
+ void LogWorldIO(const char *message);
+ void LogWorldName(const char *prefix, const std::wstring &name);
+}
diff --git a/Minecraft.Server/ServerProperties.cpp b/Minecraft.Server/ServerProperties.cpp
new file mode 100644
index 00000000..d6ba64e7
--- /dev/null
+++ b/Minecraft.Server/ServerProperties.cpp
@@ -0,0 +1,930 @@
+#include "stdafx.h"
+
+#include "ServerProperties.h"
+
+#include "ServerLogger.h"
+#include "Common\\StringUtils.h"
+#include "Common\\FileUtils.h"
+#include "..\\Minecraft.World\\ChunkSource.h"
+
+#include <cctype>
+#include <map>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unordered_map>
+
+namespace ServerRuntime
+{
+using StringUtils::ToLowerAscii;
+using StringUtils::TrimAscii;
+using StringUtils::StripUtf8Bom;
+using StringUtils::Utf8ToWide;
+using StringUtils::WideToUtf8;
+
+struct ServerPropertyDefault
+{
+ const char *key;
+ const char *value;
+};
+
+static const char *kServerPropertiesPath = "server.properties";
+static const size_t kMaxSaveIdLength = 31;
+
+static const int kDefaultServerPort = 25565;
+static const int kDefaultMaxPlayers = 16;
+static const int kMaxDedicatedPlayers = 256;
+static const int kDefaultAutosaveIntervalSeconds = 60;
+static const char *kLanAdvertisePropertyKey = "lan-advertise";
+
+static const ServerPropertyDefault kServerPropertyDefaults[] =
+{
+ { "allow-flight", "true" },
+ { "allow-nether", "true" },
+ { "autosave-interval", "60" },
+ { "bedrock-fog", "true" },
+ { "bonus-chest", "false" },
+ { "difficulty", "1" },
+ { "disable-saving", "false" },
+ { "do-daylight-cycle", "true" },
+ { "do-mob-loot", "true" },
+ { "do-mob-spawning", "true" },
+ { "do-tile-drops", "true" },
+ { "fire-spreads", "true" },
+ { "friends-of-friends", "false" },
+ { "gamemode", "0" },
+ { "gamertags", "true" },
+ { "generate-structures", "true" },
+ { "host-can-be-invisible", "true" },
+ { "host-can-change-hunger", "true" },
+ { "host-can-fly", "true" },
+ { "keep-inventory", "false" },
+ { "level-id", "world" },
+ { "level-name", "world" },
+ { "level-seed", "" },
+ { "level-type", "default" },
+ { "world-size", "classic" },
+ { "spawn-protection", "0" },
+ { "log-level", "info" },
+ { "max-build-height", "256" },
+ { "max-players", "16" },
+ { "mob-griefing", "true" },
+ { "motd", "A Minecraft Server" },
+ { "natural-regeneration", "true" },
+ { "pvp", "true" },
+ { "server-ip", "0.0.0.0" },
+ { "server-name", "DedicatedServer" },
+ { "server-port", "25565" },
+ { "white-list", "false" },
+ { "lan-advertise", "false" },
+ { "spawn-animals", "true" },
+ { "spawn-monsters", "true" },
+ { "spawn-npcs", "true" },
+ { "tnt", "true" },
+ { "trust-players", "true" }
+};
+
+static std::string BoolToString(bool value)
+{
+ return value ? "true" : "false";
+}
+
+static std::string IntToString(int value)
+{
+ char buffer[32] = {};
+ sprintf_s(buffer, sizeof(buffer), "%d", value);
+ return std::string(buffer);
+}
+
+static std::string Int64ToString(__int64 value)
+{
+ char buffer[64] = {};
+ _i64toa_s(value, buffer, sizeof(buffer), 10);
+ return std::string(buffer);
+}
+
+static int ClampInt(int value, int minValue, int maxValue)
+{
+ if (value < minValue)
+ {
+ return minValue;
+ }
+ if (value > maxValue)
+ {
+ return maxValue;
+ }
+ return value;
+}
+
+static bool TryParseBool(const std::string &value, bool *outValue)
+{
+ if (outValue == NULL)
+ {
+ return false;
+ }
+
+ std::string lowered = ToLowerAscii(TrimAscii(value));
+ if (lowered == "true" || lowered == "1" || lowered == "yes" || lowered == "on")
+ {
+ *outValue = true;
+ return true;
+ }
+ if (lowered == "false" || lowered == "0" || lowered == "no" || lowered == "off")
+ {
+ *outValue = false;
+ return true;
+ }
+ return false;
+}
+
+static bool TryParseInt(const std::string &value, int *outValue)
+{
+ if (outValue == NULL)
+ {
+ return false;
+ }
+
+ std::string trimmed = TrimAscii(value);
+ if (trimmed.empty())
+ {
+ return false;
+ }
+
+ char *end = NULL;
+ long parsed = strtol(trimmed.c_str(), &end, 10);
+ if (end == trimmed.c_str() || *end != 0)
+ {
+ return false;
+ }
+
+ *outValue = (int)parsed;
+ return true;
+}
+
+static bool TryParseInt64(const std::string &value, __int64 *outValue)
+{
+ if (outValue == NULL)
+ {
+ return false;
+ }
+
+ std::string trimmed = TrimAscii(value);
+ if (trimmed.empty())
+ {
+ return false;
+ }
+
+ char *end = NULL;
+ __int64 parsed = _strtoi64(trimmed.c_str(), &end, 10);
+ if (end == trimmed.c_str() || *end != 0)
+ {
+ return false;
+ }
+
+ *outValue = parsed;
+ return true;
+}
+
+static std::string LogLevelToPropertyValue(EServerLogLevel level)
+{
+ switch (level)
+ {
+ case eServerLogLevel_Debug:
+ return "debug";
+ case eServerLogLevel_Warn:
+ return "warn";
+ case eServerLogLevel_Error:
+ return "error";
+ case eServerLogLevel_Info:
+ default:
+ return "info";
+ }
+}
+
+/**
+ * **Normalize Save ID**
+ *
+ * Normalizes an arbitrary string into a safe save destination ID
+ * Conversion rules:
+ * - Lowercase alphabetic characters
+ * - Keep only `[a-z0-9_.-]`
+ * - Replace spaces and unsupported characters with `_`
+ * - Fallback to `world` when empty
+ * - Enforce max length to match storage constraints
+ * 保存先IDの正規化処理
+ */
+static std::string NormalizeSaveId(const std::string &source)
+{
+ std::string out;
+ out.reserve(source.length());
+
+ // Normalize into a character set that is safe for storage save IDs
+ // Replace invalid characters with '_' and fold letter case to reduce collisions
+ for (size_t i = 0; i < source.length(); ++i)
+ {
+ unsigned char ch = (unsigned char)source[i];
+ if (ch >= 'A' && ch <= 'Z')
+ {
+ ch = (unsigned char)(ch - 'A' + 'a');
+ }
+
+ const bool alnum = (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');
+ const bool passthrough = (ch == '_') || (ch == '-') || (ch == '.');
+ if (alnum || passthrough)
+ {
+ out.push_back((char)ch);
+ }
+ else if (std::isspace(ch))
+ {
+ out.push_back('_');
+ }
+ else if (ch < 0x80)
+ {
+ out.push_back('_');
+ }
+ }
+
+ if (out.empty())
+ {
+ out = "world";
+ }
+
+ // Add a prefix when needed to avoid awkward leading characters
+ if (!((out[0] >= 'a' && out[0] <= 'z') || (out[0] >= '0' && out[0] <= '9')))
+ {
+ out = std::string("w_") + out;
+ }
+
+ // Clamp length to the 4J-side filename buffer constraint
+ if (out.length() > kMaxSaveIdLength)
+ {
+ out.resize(kMaxSaveIdLength);
+ }
+
+ return out;
+}
+
+static void ApplyDefaultServerProperties(std::unordered_map<std::string, std::string> *properties)
+{
+ if (properties == NULL)
+ {
+ return;
+ }
+
+ const size_t defaultCount = sizeof(kServerPropertyDefaults) / sizeof(kServerPropertyDefaults[0]);
+ for (size_t i = 0; i < defaultCount; ++i)
+ {
+ (*properties)[kServerPropertyDefaults[i].key] = kServerPropertyDefaults[i].value;
+ }
+}
+
+/**
+ * **Parse server.properties Text**
+ *
+ * Extracts key/value pairs from `server.properties` format text
+ * - Ignores lines starting with `#` or `!` as comments
+ * - Accepts `=` or `:` as separators
+ * - Skips invalid lines and continues
+ * server.propertiesのパース処理
+ */
+static bool ReadServerPropertiesFile(const char *filePath, std::unordered_map<std::string, std::string> *properties, int *outParsedCount)
+{
+ if (properties == NULL)
+ {
+ return false;
+ }
+
+ std::string text;
+ if (filePath == NULL || !FileUtils::ReadTextFile(filePath, &text))
+ {
+ return false;
+ }
+
+ text = StripUtf8Bom(text);
+
+ int parsedCount = 0;
+ for (size_t start = 0; start <= text.length();)
+ {
+ size_t end = text.find_first_of("\r\n", start);
+ size_t nextStart = text.length() + 1;
+ if (end != std::string::npos)
+ {
+ nextStart = end + 1;
+ if (text[end] == '\r' && nextStart < text.length() && text[nextStart] == '\n')
+ {
+ ++nextStart;
+ }
+ }
+
+ std::string line;
+ if (end == std::string::npos)
+ {
+ line = text.substr(start);
+ }
+ else
+ {
+ line = text.substr(start, end - start);
+ }
+
+ std::string trimmedLine = TrimAscii(line);
+ if (trimmedLine.empty())
+ {
+ start = nextStart;
+ continue;
+ }
+
+ if (trimmedLine[0] == '#' || trimmedLine[0] == '!')
+ {
+ start = nextStart;
+ continue;
+ }
+
+ size_t eqPos = trimmedLine.find('=');
+ size_t colonPos = trimmedLine.find(':');
+ size_t sepPos = std::string::npos;
+ if (eqPos == std::string::npos)
+ {
+ sepPos = colonPos;
+ }
+ else if (colonPos == std::string::npos)
+ {
+ sepPos = eqPos;
+ }
+ else
+ {
+ sepPos = (eqPos < colonPos) ? eqPos : colonPos;
+ }
+
+ if (sepPos == std::string::npos)
+ {
+ start = nextStart;
+ continue;
+ }
+
+ std::string key = TrimAscii(trimmedLine.substr(0, sepPos));
+ if (key.empty())
+ {
+ start = nextStart;
+ continue;
+ }
+
+ std::string value = TrimAscii(trimmedLine.substr(sepPos + 1));
+ (*properties)[key] = value;
+ ++parsedCount;
+ start = nextStart;
+ }
+
+ if (outParsedCount != NULL)
+ {
+ *outParsedCount = parsedCount;
+ }
+
+ return true;
+}
+
+/**
+ * **Write server.properties Text**
+ *
+ * Writes key/value data back as `server.properties`
+ * Sorts keys before writing to keep output order stable
+ * server.propertiesの書き戻し処理
+ */
+static bool WriteServerPropertiesFile(const char *filePath, const std::unordered_map<std::string, std::string> &properties)
+{
+ if (filePath == NULL)
+ {
+ return false;
+ }
+
+ std::string text;
+ text += "# Minecraft server properties\n";
+ text += "# Auto-generated and normalized when missing\n";
+
+ std::map<std::string, std::string> sortedProperties(properties.begin(), properties.end());
+ for (std::map<std::string, std::string>::const_iterator it = sortedProperties.begin(); it != sortedProperties.end(); ++it)
+ {
+ text += it->first;
+ text += "=";
+ text += it->second;
+ text += "\n";
+ }
+
+ return FileUtils::WriteTextFileAtomic(filePath, text);
+}
+
+static bool ReadNormalizedBoolProperty(
+ std::unordered_map<std::string, std::string> *properties,
+ const char *key,
+ bool defaultValue,
+ bool *shouldWrite)
+{
+ std::string raw = TrimAscii((*properties)[key]);
+ bool value = defaultValue;
+ if (!TryParseBool(raw, &value))
+ {
+ value = defaultValue;
+ }
+
+ std::string normalized = BoolToString(value);
+ if (raw != normalized)
+ {
+ (*properties)[key] = normalized;
+ if (shouldWrite != NULL)
+ {
+ *shouldWrite = true;
+ }
+ }
+
+ return value;
+}
+
+static int ReadNormalizedIntProperty(
+ std::unordered_map<std::string, std::string> *properties,
+ const char *key,
+ int defaultValue,
+ int minValue,
+ int maxValue,
+ bool *shouldWrite)
+{
+ std::string raw = TrimAscii((*properties)[key]);
+ int value = defaultValue;
+ if (!TryParseInt(raw, &value))
+ {
+ value = defaultValue;
+ }
+ value = ClampInt(value, minValue, maxValue);
+
+ std::string normalized = IntToString(value);
+ if (raw != normalized)
+ {
+ (*properties)[key] = normalized;
+ if (shouldWrite != NULL)
+ {
+ *shouldWrite = true;
+ }
+ }
+
+ return value;
+}
+
+static std::string ReadNormalizedStringProperty(
+ std::unordered_map<std::string, std::string> *properties,
+ const char *key,
+ const std::string &defaultValue,
+ size_t maxLength,
+ bool *shouldWrite)
+{
+ std::string value = TrimAscii((*properties)[key]);
+ if (value.empty())
+ {
+ value = defaultValue;
+ }
+ if (maxLength > 0 && value.length() > maxLength)
+ {
+ value.resize(maxLength);
+ }
+
+ if (value != (*properties)[key])
+ {
+ (*properties)[key] = value;
+ if (shouldWrite != NULL)
+ {
+ *shouldWrite = true;
+ }
+ }
+
+ return value;
+}
+
+static bool ReadNormalizedOptionalInt64Property(
+ std::unordered_map<std::string, std::string> *properties,
+ const char *key,
+ __int64 *outValue,
+ bool *shouldWrite)
+{
+ std::string raw = TrimAscii((*properties)[key]);
+ if (raw.empty())
+ {
+ if ((*properties)[key] != "")
+ {
+ (*properties)[key] = "";
+ if (shouldWrite != NULL)
+ {
+ *shouldWrite = true;
+ }
+ }
+ return false;
+ }
+
+ __int64 parsed = 0;
+ if (!TryParseInt64(raw, &parsed))
+ {
+ (*properties)[key] = "";
+ if (shouldWrite != NULL)
+ {
+ *shouldWrite = true;
+ }
+ return false;
+ }
+
+ std::string normalized = Int64ToString(parsed);
+ if (raw != normalized)
+ {
+ (*properties)[key] = normalized;
+ if (shouldWrite != NULL)
+ {
+ *shouldWrite = true;
+ }
+ }
+
+ if (outValue != NULL)
+ {
+ *outValue = parsed;
+ }
+ return true;
+}
+
+static EServerLogLevel ReadNormalizedLogLevelProperty(
+ std::unordered_map<std::string, std::string> *properties,
+ const char *key,
+ EServerLogLevel defaultValue,
+ bool *shouldWrite)
+{
+ std::string raw = TrimAscii((*properties)[key]);
+ EServerLogLevel value = defaultValue;
+ if (!TryParseServerLogLevel(raw.c_str(), &value))
+ {
+ value = defaultValue;
+ }
+
+ std::string normalized = LogLevelToPropertyValue(value);
+ if (raw != normalized)
+ {
+ (*properties)[key] = normalized;
+ if (shouldWrite != NULL)
+ {
+ *shouldWrite = true;
+ }
+ }
+
+ return value;
+}
+
+static std::string ReadNormalizedLevelTypeProperty(
+ std::unordered_map<std::string, std::string> *properties,
+ const char *key,
+ bool *outIsFlat,
+ bool *shouldWrite)
+{
+ std::string raw = TrimAscii((*properties)[key]);
+ std::string lowered = ToLowerAscii(raw);
+
+ bool isFlat = false;
+ std::string normalized = "default";
+ if (lowered == "flat" || lowered == "superflat" || lowered == "1")
+ {
+ isFlat = true;
+ normalized = "flat";
+ }
+ else if (lowered == "default" || lowered == "normal" || lowered == "0")
+ {
+ isFlat = false;
+ normalized = "default";
+ }
+
+ if (raw != normalized)
+ {
+ (*properties)[key] = normalized;
+ if (shouldWrite != NULL)
+ {
+ *shouldWrite = true;
+ }
+ }
+
+ if (outIsFlat != NULL)
+ {
+ *outIsFlat = isFlat;
+ }
+
+ return normalized;
+}
+
+static std::string WorldSizeToPropertyValue(int worldSize)
+{
+ switch (worldSize)
+ {
+ case e_worldSize_Small:
+ return "small";
+ case e_worldSize_Medium:
+ return "medium";
+ case e_worldSize_Large:
+ return "large";
+ case e_worldSize_Classic:
+ default:
+ return "classic";
+ }
+}
+
+static int WorldSizeToXzChunks(int worldSize)
+{
+ switch (worldSize)
+ {
+ case e_worldSize_Small:
+ return LEVEL_WIDTH_SMALL;
+ case e_worldSize_Medium:
+ return LEVEL_WIDTH_MEDIUM;
+ case e_worldSize_Large:
+ return LEVEL_WIDTH_LARGE;
+ case e_worldSize_Classic:
+ default:
+ return LEVEL_WIDTH_CLASSIC;
+ }
+}
+
+static int WorldSizeToHellScale(int worldSize)
+{
+ switch (worldSize)
+ {
+ case e_worldSize_Small:
+ return HELL_LEVEL_SCALE_SMALL;
+ case e_worldSize_Medium:
+ return HELL_LEVEL_SCALE_MEDIUM;
+ case e_worldSize_Large:
+ return HELL_LEVEL_SCALE_LARGE;
+ case e_worldSize_Classic:
+ default:
+ return HELL_LEVEL_SCALE_CLASSIC;
+ }
+}
+
+static bool TryParseWorldSize(const std::string &lowered, int *outWorldSize)
+{
+ if (outWorldSize == NULL)
+ {
+ return false;
+ }
+
+ if (lowered == "classic" || lowered == "54" || lowered == "1")
+ {
+ *outWorldSize = e_worldSize_Classic;
+ return true;
+ }
+ if (lowered == "small" || lowered == "64" || lowered == "2")
+ {
+ *outWorldSize = e_worldSize_Small;
+ return true;
+ }
+ if (lowered == "medium" || lowered == "192" || lowered == "3")
+ {
+ *outWorldSize = e_worldSize_Medium;
+ return true;
+ }
+ if (lowered == "large" || lowered == "320" || lowered == "4")
+ {
+ *outWorldSize = e_worldSize_Large;
+ return true;
+ }
+
+ return false;
+}
+
+static int ReadNormalizedWorldSizeProperty(
+ std::unordered_map<std::string, std::string> *properties,
+ const char *key,
+ int defaultWorldSize,
+ int *outXzChunks,
+ int *outHellScale,
+ bool *shouldWrite)
+{
+ std::string raw = TrimAscii((*properties)[key]);
+ std::string lowered = ToLowerAscii(raw);
+
+ int worldSize = defaultWorldSize;
+ if (!raw.empty())
+ {
+ int parsedWorldSize = defaultWorldSize;
+ if (TryParseWorldSize(lowered, &parsedWorldSize))
+ {
+ worldSize = parsedWorldSize;
+ }
+ }
+
+ std::string normalized = WorldSizeToPropertyValue(worldSize);
+ if (raw != normalized)
+ {
+ (*properties)[key] = normalized;
+ if (shouldWrite != NULL)
+ {
+ *shouldWrite = true;
+ }
+ }
+
+ if (outXzChunks != NULL)
+ {
+ *outXzChunks = WorldSizeToXzChunks(worldSize);
+ }
+ if (outHellScale != NULL)
+ {
+ *outHellScale = WorldSizeToHellScale(worldSize);
+ }
+
+ return worldSize;
+}
+
+/**
+ * **Load Effective Server Properties Config**
+ *
+ * Loads effective world settings, repairs missing or invalid values, and returns normalized config
+ * - Creates defaults when file is missing
+ * - Fills required keys when absent
+ * - Normalizes `level-id` to a safe format
+ * - Auto-saves when any fix is applied
+ * 実効設定の読み込みと補正処理
+ */
+ServerPropertiesConfig LoadServerPropertiesConfig()
+{
+ ServerPropertiesConfig config;
+
+ std::unordered_map<std::string, std::string> defaults;
+ std::unordered_map<std::string, std::string> loaded;
+ ApplyDefaultServerProperties(&defaults);
+
+ int parsedCount = 0;
+ bool readSuccess = ReadServerPropertiesFile(kServerPropertiesPath, &loaded, &parsedCount);
+ std::unordered_map<std::string, std::string> merged = defaults;
+ bool shouldWrite = false;
+
+ if (!readSuccess)
+ {
+ LogWorldIO("server.properties not found or unreadable; creating defaults");
+ shouldWrite = true;
+ }
+ else
+ {
+ if (parsedCount == 0)
+ {
+ LogWorldIO("server.properties has no properties; applying defaults");
+ shouldWrite = true;
+ }
+
+ const size_t defaultCount = sizeof(kServerPropertyDefaults) / sizeof(kServerPropertyDefaults[0]);
+ for (size_t i = 0; i < defaultCount; ++i)
+ {
+ if (loaded.find(kServerPropertyDefaults[i].key) == loaded.end())
+ {
+ shouldWrite = true;
+ break;
+ }
+ }
+ }
+
+ for (std::unordered_map<std::string, std::string>::const_iterator it = loaded.begin(); it != loaded.end(); ++it)
+ {
+ // Merge loaded values over defaults and keep unknown keys whenever possible
+ merged[it->first] = it->second;
+ }
+
+ std::string worldName = TrimAscii(merged["level-name"]);
+ if (worldName.empty())
+ {
+ worldName = "world";
+ shouldWrite = true;
+ }
+
+ std::string worldSaveId = TrimAscii(merged["level-id"]);
+ if (worldSaveId.empty())
+ {
+ // If level-id is missing, derive it from level-name to lock save destination
+ worldSaveId = NormalizeSaveId(worldName);
+ shouldWrite = true;
+ }
+ else
+ {
+ // Normalize existing level-id as well to avoid future inconsistencies
+ std::string normalized = NormalizeSaveId(worldSaveId);
+ if (normalized != worldSaveId)
+ {
+ worldSaveId = normalized;
+ shouldWrite = true;
+ }
+ }
+
+ merged["level-name"] = worldName;
+ merged["level-id"] = worldSaveId;
+
+ config.worldName = Utf8ToWide(worldName.c_str());
+ config.worldSaveId = worldSaveId;
+
+ config.serverPort = ReadNormalizedIntProperty(&merged, "server-port", kDefaultServerPort, 1, 65535, &shouldWrite);
+ config.serverIp = ReadNormalizedStringProperty(&merged, "server-ip", "0.0.0.0", 255, &shouldWrite);
+ config.lanAdvertise = ReadNormalizedBoolProperty(&merged, kLanAdvertisePropertyKey, false, &shouldWrite);
+ config.whiteListEnabled = ReadNormalizedBoolProperty(&merged, "white-list", false, &shouldWrite);
+ config.serverName = ReadNormalizedStringProperty(&merged, "server-name", "DedicatedServer", 16, &shouldWrite);
+ config.maxPlayers = ReadNormalizedIntProperty(&merged, "max-players", kDefaultMaxPlayers, 1, kMaxDedicatedPlayers, &shouldWrite);
+ config.seed = 0;
+ config.hasSeed = ReadNormalizedOptionalInt64Property(&merged, "level-seed", &config.seed, &shouldWrite);
+ config.logLevel = ReadNormalizedLogLevelProperty(&merged, "log-level", eServerLogLevel_Info, &shouldWrite);
+ config.autosaveIntervalSeconds = ReadNormalizedIntProperty(&merged, "autosave-interval", kDefaultAutosaveIntervalSeconds, 5, 3600, &shouldWrite);
+
+ config.difficulty = ReadNormalizedIntProperty(&merged, "difficulty", 1, 0, 3, &shouldWrite);
+ config.gameMode = ReadNormalizedIntProperty(&merged, "gamemode", 0, 0, 1, &shouldWrite);
+ config.worldSize = ReadNormalizedWorldSizeProperty(
+ &merged,
+ "world-size",
+ e_worldSize_Classic,
+ &config.worldSizeChunks,
+ &config.worldHellScale,
+ &shouldWrite);
+ config.levelType = ReadNormalizedLevelTypeProperty(&merged, "level-type", &config.levelTypeFlat, &shouldWrite);
+ config.spawnProtectionRadius = ReadNormalizedIntProperty(&merged, "spawn-protection", 0, 0, 256, &shouldWrite);
+ config.generateStructures = ReadNormalizedBoolProperty(&merged, "generate-structures", true, &shouldWrite);
+ config.bonusChest = ReadNormalizedBoolProperty(&merged, "bonus-chest", false, &shouldWrite);
+ config.pvp = ReadNormalizedBoolProperty(&merged, "pvp", true, &shouldWrite);
+ config.trustPlayers = ReadNormalizedBoolProperty(&merged, "trust-players", true, &shouldWrite);
+ config.fireSpreads = ReadNormalizedBoolProperty(&merged, "fire-spreads", true, &shouldWrite);
+ config.tnt = ReadNormalizedBoolProperty(&merged, "tnt", true, &shouldWrite);
+ config.spawnAnimals = ReadNormalizedBoolProperty(&merged, "spawn-animals", true, &shouldWrite);
+ config.spawnNpcs = ReadNormalizedBoolProperty(&merged, "spawn-npcs", true, &shouldWrite);
+ config.spawnMonsters = ReadNormalizedBoolProperty(&merged, "spawn-monsters", true, &shouldWrite);
+ config.allowFlight = ReadNormalizedBoolProperty(&merged, "allow-flight", true, &shouldWrite);
+ config.allowNether = ReadNormalizedBoolProperty(&merged, "allow-nether", true, &shouldWrite);
+ config.friendsOfFriends = ReadNormalizedBoolProperty(&merged, "friends-of-friends", false, &shouldWrite);
+ config.gamertags = ReadNormalizedBoolProperty(&merged, "gamertags", true, &shouldWrite);
+ config.bedrockFog = ReadNormalizedBoolProperty(&merged, "bedrock-fog", true, &shouldWrite);
+ config.hostCanFly = ReadNormalizedBoolProperty(&merged, "host-can-fly", true, &shouldWrite);
+ config.hostCanChangeHunger = ReadNormalizedBoolProperty(&merged, "host-can-change-hunger", true, &shouldWrite);
+ config.hostCanBeInvisible = ReadNormalizedBoolProperty(&merged, "host-can-be-invisible", true, &shouldWrite);
+ config.disableSaving = ReadNormalizedBoolProperty(&merged, "disable-saving", false, &shouldWrite);
+ config.mobGriefing = ReadNormalizedBoolProperty(&merged, "mob-griefing", true, &shouldWrite);
+ config.keepInventory = ReadNormalizedBoolProperty(&merged, "keep-inventory", false, &shouldWrite);
+ config.doMobSpawning = ReadNormalizedBoolProperty(&merged, "do-mob-spawning", true, &shouldWrite);
+ config.doMobLoot = ReadNormalizedBoolProperty(&merged, "do-mob-loot", true, &shouldWrite);
+ config.doTileDrops = ReadNormalizedBoolProperty(&merged, "do-tile-drops", true, &shouldWrite);
+ config.naturalRegeneration = ReadNormalizedBoolProperty(&merged, "natural-regeneration", true, &shouldWrite);
+ config.doDaylightCycle = ReadNormalizedBoolProperty(&merged, "do-daylight-cycle", true, &shouldWrite);
+
+ config.maxBuildHeight = ReadNormalizedIntProperty(&merged, "max-build-height", 256, 64, 256, &shouldWrite);
+ config.motd = ReadNormalizedStringProperty(&merged, "motd", "A Minecraft Server", 255, &shouldWrite);
+
+ if (shouldWrite)
+ {
+ if (WriteServerPropertiesFile(kServerPropertiesPath, merged))
+ {
+ LogWorldIO("wrote server.properties");
+ }
+ else
+ {
+ LogWorldIO("failed to write server.properties");
+ }
+ }
+
+ return config;
+}
+
+/**
+ * **Save World Identity While Preserving Other Keys**
+ *
+ * Saves world identity fields while preserving as many other settings as possible
+ * - Reads existing file and merges including unknown keys
+ * - Updates `level-name`, `level-id`, and `white-list` before writing back
+ * ワールド識別情報の保存処理
+ */
+bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config)
+{
+ std::unordered_map<std::string, std::string> merged;
+ ApplyDefaultServerProperties(&merged);
+
+ std::unordered_map<std::string, std::string> loaded;
+ int parsedCount = 0;
+ if (ReadServerPropertiesFile(kServerPropertiesPath, &loaded, &parsedCount))
+ {
+ for (std::unordered_map<std::string, std::string>::const_iterator it = loaded.begin(); it != loaded.end(); ++it)
+ {
+ // Keep existing content so keys untouched by caller are not dropped
+ merged[it->first] = it->second;
+ }
+ }
+
+ std::string worldName = TrimAscii(WideToUtf8(config.worldName));
+ if (worldName.empty())
+ {
+ worldName = "world"; // Default world name
+ }
+
+ std::string worldSaveId = TrimAscii(config.worldSaveId);
+ if (worldSaveId.empty())
+ {
+ worldSaveId = NormalizeSaveId(worldName);
+ }
+ else
+ {
+ worldSaveId = NormalizeSaveId(worldSaveId);
+ }
+
+ merged["level-name"] = worldName;
+ merged["level-id"] = worldSaveId;
+ merged["white-list"] = BoolToString(config.whiteListEnabled);
+
+ return WriteServerPropertiesFile(kServerPropertiesPath, merged);
+}
+}
+
diff --git a/Minecraft.Server/ServerProperties.h b/Minecraft.Server/ServerProperties.h
new file mode 100644
index 00000000..3bb5aca8
--- /dev/null
+++ b/Minecraft.Server/ServerProperties.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <string>
+#include "ServerLogger.h"
+
+namespace ServerRuntime
+{
+ /**
+ * `server.properties`
+ */
+ struct ServerPropertiesConfig
+ {
+ /** world name `level-name` */
+ std::wstring worldName;
+ /** world save id `level-id` */
+ std::string worldSaveId;
+
+ /** `server-port` */
+ int serverPort;
+ /** `server-ip` */
+ std::string serverIp;
+ /** `lan-advertise` */
+ bool lanAdvertise;
+ /** `white-list` */
+ bool whiteListEnabled;
+ /** `server-name` (max 16 chars at runtime) */
+ std::string serverName;
+ /** `max-players` */
+ int maxPlayers;
+ /** `level-seed` is explicitly set */
+ bool hasSeed;
+ /** `level-seed` */
+ __int64 seed;
+ /** `log-level` */
+ EServerLogLevel logLevel;
+ /** `autosave-interval` (seconds) */
+ int autosaveIntervalSeconds;
+
+ /** host options / game settings */
+ int difficulty;
+ int gameMode;
+ /** `world-size` preset (`classic` / `small` / `medium` / `large`) */
+ int worldSize;
+ /** Overworld chunk width derived from `world-size` */
+ int worldSizeChunks;
+ /** Nether scale derived from `world-size` */
+ int worldHellScale;
+ bool levelTypeFlat;
+ /** `spawn-protection` radius in blocks (0 disables protection) */
+ int spawnProtectionRadius;
+ bool generateStructures;
+ bool bonusChest;
+ bool pvp;
+ bool trustPlayers;
+ bool fireSpreads;
+ bool tnt;
+ bool spawnAnimals;
+ bool spawnNpcs;
+ bool spawnMonsters;
+ bool allowFlight;
+ bool allowNether;
+ bool friendsOfFriends;
+ bool gamertags;
+ bool bedrockFog;
+ bool hostCanFly;
+ bool hostCanChangeHunger;
+ bool hostCanBeInvisible;
+ bool disableSaving;
+ bool mobGriefing;
+ bool keepInventory;
+ bool doMobSpawning;
+ bool doMobLoot;
+ bool doTileDrops;
+ bool naturalRegeneration;
+ bool doDaylightCycle;
+
+ /** other MinecraftServer runtime settings */
+ int maxBuildHeight;
+ std::string levelType;
+ std::string motd;
+ };
+
+ /**
+ * server.properties loader
+ *
+ * - ファイル欠損時はデフォルト値で新規作成
+ * - 必須キー不足時は補完して再保存
+ * - `level-id` は保存先として安全な形式へ正規化
+ *
+ * @return `WorldManager` が利用するワールド設定
+ */
+ ServerPropertiesConfig LoadServerPropertiesConfig();
+
+ /**
+ * server.properties saver
+ *
+ * - `level-name` と `level-id` を更新
+ * - `white-list` を更新
+ * - それ以外の既存キーは極力保持
+ *
+ * @param config 保存するワールド識別情報と永続化対象設定
+ * @return 書き込み成功時 `true`
+ */
+ bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config);
+}
diff --git a/Minecraft.Server/ServerShutdown.h b/Minecraft.Server/ServerShutdown.h
new file mode 100644
index 00000000..302b1a55
--- /dev/null
+++ b/Minecraft.Server/ServerShutdown.h
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace ServerRuntime
+{
+ void RequestDedicatedServerShutdown();
+}
diff --git a/Minecraft.Server/Windows64/ServerMain.cpp b/Minecraft.Server/Windows64/ServerMain.cpp
new file mode 100644
index 00000000..a8d5fc66
--- /dev/null
+++ b/Minecraft.Server/Windows64/ServerMain.cpp
@@ -0,0 +1,718 @@
+#include "stdafx.h"
+
+#include "Common/App_Defines.h"
+#include "Common/Network/GameNetworkManager.h"
+#include "Input.h"
+#include "Minecraft.h"
+#include "MinecraftServer.h"
+#include "..\Access\Access.h"
+#include "..\Common\StringUtils.h"
+#include "..\ServerLogger.h"
+#include "..\ServerLogManager.h"
+#include "..\ServerProperties.h"
+#include "..\ServerShutdown.h"
+#include "..\WorldManager.h"
+#include "..\Console\ServerCli.h"
+#include "Tesselator.h"
+#include "Windows64/4JLibs/inc/4J_Render.h"
+#include "Windows64/GameConfig/Minecraft.spa.h"
+#include "Windows64/KeyboardMouseInput.h"
+#include "Windows64/Network/WinsockNetLayer.h"
+#include "Windows64/Windows64_UIController.h"
+#include "Common/UI/UI.h"
+
+#include "../../Minecraft.World/AABB.h"
+#include "../../Minecraft.World/Vec3.h"
+#include "../../Minecraft.World/IntCache.h"
+#include "../../Minecraft.World/ChunkSource.h"
+#include "../../Minecraft.World/TilePos.h"
+#include "../../Minecraft.World/compression.h"
+#include "../../Minecraft.World/OldChunkStorage.h"
+#include "../../Minecraft.World/net.minecraft.world.level.tile.h"
+#include "../../Minecraft.World/Random.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atomic>
+
+extern ATOM MyRegisterClass(HINSTANCE hInstance);
+extern BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);
+extern HRESULT InitDevice();
+extern void CleanupDevice();
+extern void DefineActions(void);
+
+extern HWND g_hWnd;
+extern int g_iScreenWidth;
+extern int g_iScreenHeight;
+extern char g_Win64Username[17];
+extern wchar_t g_Win64UsernameW[17];
+extern ID3D11Device* g_pd3dDevice;
+extern ID3D11DeviceContext* g_pImmediateContext;
+extern IDXGISwapChain* g_pSwapChain;
+extern ID3D11RenderTargetView* g_pRenderTargetView;
+extern ID3D11DepthStencilView* g_pDepthStencilView;
+extern DWORD dwProfileSettingsA[];
+
+static const int kProfileValueCount = 5;
+static const int kProfileSettingCount = 4;
+
+struct DedicatedServerConfig
+{
+ int port;
+ char bindIP[256];
+ char name[17];
+ int maxPlayers;
+ int worldSize;
+ int worldSizeChunks;
+ int worldHellScale;
+ __int64 seed;
+ ServerRuntime::EServerLogLevel logLevel;
+ bool hasSeed;
+ bool showHelp;
+};
+
+static std::atomic<bool> g_shutdownRequested(false);
+static const DWORD kDefaultAutosaveIntervalMs = 60 * 1000;
+static const int kServerActionPad = 0;
+
+static bool IsShutdownRequested()
+{
+ return g_shutdownRequested.load();
+}
+
+namespace ServerRuntime
+{
+ void RequestDedicatedServerShutdown()
+ {
+ g_shutdownRequested.store(true);
+ }
+}
+
+/**
+ * Calls Access::Shutdown automatically once dedicated access control was initialized successfully
+ * アクセス制御初期化後のShutdownを自動化する
+ */
+class AccessShutdownGuard
+{
+public:
+ AccessShutdownGuard()
+ : m_active(false)
+ {
+ }
+
+ void Activate()
+ {
+ m_active = true;
+ }
+
+ ~AccessShutdownGuard()
+ {
+ if (m_active)
+ {
+ ServerRuntime::Access::Shutdown();
+ }
+ }
+
+private:
+ bool m_active;
+};
+static BOOL WINAPI ConsoleCtrlHandlerProc(DWORD ctrlType)
+{
+ switch (ctrlType)
+ {
+ case CTRL_C_EVENT:
+ case CTRL_BREAK_EVENT:
+ case CTRL_CLOSE_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ ServerRuntime::RequestDedicatedServerShutdown();
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/**
+ * **Wait For Server Stopped Signal**
+ *
+ * Thread entry used during shutdown to wait until the network layer reports server stop completion
+ * 停止通知待機用の終了スレッド処理
+ */
+static int WaitForServerStoppedThreadProc(void *)
+{
+ if (g_NetworkManager.ServerStoppedValid())
+ {
+ g_NetworkManager.ServerStoppedWait();
+ }
+ return 0;
+}
+
+static void PrintUsage()
+{
+ ServerRuntime::LogInfo("usage", "Minecraft.Server.exe [options]");
+ ServerRuntime::LogInfo("usage", " -port <1-65535> Listen TCP port (default: server.properties:server-port)");
+ ServerRuntime::LogInfo("usage", " -ip <addr> Bind address (default: server.properties:server-ip)");
+ ServerRuntime::LogInfo("usage", " -bind <addr> Alias of -ip");
+ ServerRuntime::LogInfo("usage", " -name <name> Host display name (max 16 chars, default: server.properties:server-name)");
+ ServerRuntime::LogInfo("usage", " -maxplayers <1-8> Public slots (default: server.properties:max-players)");
+ ServerRuntime::LogInfo("usage", " -seed <int64> World seed (overrides server.properties:level-seed)");
+ ServerRuntime::LogInfo("usage", " -loglevel <level> debug|info|warn|error (default: server.properties:log-level)");
+ ServerRuntime::LogInfo("usage", " -help Show this help");
+}
+
+using ServerRuntime::LoadServerPropertiesConfig;
+using ServerRuntime::LogError;
+using ServerRuntime::LogErrorf;
+using ServerRuntime::LogInfof;
+using ServerRuntime::LogDebugf;
+using ServerRuntime::LogStartupStep;
+using ServerRuntime::LogWarn;
+using ServerRuntime::LogWorldIO;
+using ServerRuntime::SaveServerPropertiesConfig;
+using ServerRuntime::SetServerLogLevel;
+using ServerRuntime::ServerPropertiesConfig;
+using ServerRuntime::TryParseServerLogLevel;
+using ServerRuntime::StringUtils::WideToUtf8;
+using ServerRuntime::BootstrapWorldForServer;
+using ServerRuntime::eWorldBootstrap_CreatedNew;
+using ServerRuntime::eWorldBootstrap_Failed;
+using ServerRuntime::eWorldBootstrap_Loaded;
+using ServerRuntime::WaitForWorldActionIdle;
+using ServerRuntime::WorldBootstrapResult;
+
+static bool ParseIntArg(const char *value, int *outValue)
+{
+ if (value == NULL || *value == 0)
+ return false;
+
+ char *end = NULL;
+ long parsed = strtol(value, &end, 10);
+ if (end == value || *end != 0)
+ return false;
+
+ *outValue = (int)parsed;
+ return true;
+}
+
+static bool ParseInt64Arg(const char *value, __int64 *outValue)
+{
+ if (value == NULL || *value == 0)
+ return false;
+
+ char *end = NULL;
+ __int64 parsed = _strtoi64(value, &end, 10);
+ if (end == value || *end != 0)
+ return false;
+
+ *outValue = parsed;
+ return true;
+}
+
+static bool ParseCommandLine(int argc, char **argv, DedicatedServerConfig *config)
+{
+ for (int i = 1; i < argc; ++i)
+ {
+ const char *arg = argv[i];
+ if (_stricmp(arg, "-help") == 0 || _stricmp(arg, "--help") == 0 || _stricmp(arg, "-h") == 0)
+ {
+ config->showHelp = true;
+ return true;
+ }
+ else if ((_stricmp(arg, "-port") == 0) && (i + 1 < argc))
+ {
+ int port = 0;
+ if (!ParseIntArg(argv[++i], &port) || port <= 0 || port > 65535)
+ {
+ LogError("startup", "Invalid -port value.");
+ return false;
+ }
+ config->port = port;
+ }
+ else if ((_stricmp(arg, "-ip") == 0 || _stricmp(arg, "-bind") == 0) && (i + 1 < argc))
+ {
+ strncpy_s(config->bindIP, sizeof(config->bindIP), argv[++i], _TRUNCATE);
+ }
+ else if ((_stricmp(arg, "-name") == 0) && (i + 1 < argc))
+ {
+ strncpy_s(config->name, sizeof(config->name), argv[++i], _TRUNCATE);
+ }
+ else if ((_stricmp(arg, "-maxplayers") == 0) && (i + 1 < argc))
+ {
+ int maxPlayers = 0;
+ if (!ParseIntArg(argv[++i], &maxPlayers) || maxPlayers <= 0 || maxPlayers > MINECRAFT_NET_MAX_PLAYERS)
+ {
+ LogError("startup", "Invalid -maxplayers value.");
+ return false;
+ }
+ config->maxPlayers = maxPlayers;
+ }
+ else if ((_stricmp(arg, "-seed") == 0) && (i + 1 < argc))
+ {
+ if (!ParseInt64Arg(argv[++i], &config->seed))
+ {
+ LogError("startup", "Invalid -seed value.");
+ return false;
+ }
+ config->hasSeed = true;
+ }
+ else if ((_stricmp(arg, "-loglevel") == 0) && (i + 1 < argc))
+ {
+ if (!TryParseServerLogLevel(argv[++i], &config->logLevel))
+ {
+ LogError("startup", "Invalid -loglevel value. Use debug/info/warn/error.");
+ return false;
+ }
+ }
+ else
+ {
+ LogErrorf("startup", "Unknown or incomplete argument: %s", arg);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void SetExeWorkingDirectory()
+{
+ char exePath[MAX_PATH] = {};
+ GetModuleFileNameA(NULL, exePath, MAX_PATH);
+ char *slash = strrchr(exePath, '\\');
+ if (slash != NULL)
+ {
+ *(slash + 1) = 0;
+ SetCurrentDirectoryA(exePath);
+ }
+}
+
+static void ApplyServerPropertiesToDedicatedConfig(const ServerPropertiesConfig &serverProperties, DedicatedServerConfig *config)
+{
+ if (config == NULL)
+ {
+ return;
+ }
+
+ config->port = serverProperties.serverPort;
+ strncpy_s(
+ config->bindIP,
+ sizeof(config->bindIP),
+ serverProperties.serverIp.empty() ? "0.0.0.0" : serverProperties.serverIp.c_str(),
+ _TRUNCATE);
+ strncpy_s(
+ config->name,
+ sizeof(config->name),
+ serverProperties.serverName.empty() ? "DedicatedServer" : serverProperties.serverName.c_str(),
+ _TRUNCATE);
+ config->maxPlayers = serverProperties.maxPlayers;
+ config->worldSize = serverProperties.worldSize;
+ config->worldSizeChunks = serverProperties.worldSizeChunks;
+ config->worldHellScale = serverProperties.worldHellScale;
+ config->logLevel = serverProperties.logLevel;
+ config->hasSeed = serverProperties.hasSeed;
+ config->seed = serverProperties.seed;
+}
+
+/**
+ * **Tick Core Async Subsystems**
+ *
+ * Advances core subsystems for one frame to keep async processing alive
+ * Call continuously even inside wait loops to avoid stalling storage/profile/network work
+ * 非同期進行維持のためのティック処理
+ */
+static void TickCoreSystems()
+{
+ g_NetworkManager.DoWork();
+ ProfileManager.Tick();
+ StorageManager.Tick();
+}
+
+/**
+ * **Handle Queued XUI Server Action Once**
+ *
+ * Processes queued XUI/server action once
+ * XUIアクションの単発処理
+ */
+static void HandleXuiActions()
+{
+ app.HandleXuiActions();
+}
+
+/**
+ * Dedicated Server Entory Point
+ *
+ * 主な責務:
+ * - プロセス/描画/ネットワークの初期化
+ * - `WorldManager` によるワールドロードまたは新規作成
+ * - メインループと定期オートセーブ実行
+ * - 終了時の最終保存と各サブシステムの安全停止
+ */
+int main(int argc, char **argv)
+{
+ DedicatedServerConfig config;
+ config.port = WIN64_NET_DEFAULT_PORT;
+ strncpy_s(config.bindIP, sizeof(config.bindIP), "0.0.0.0", _TRUNCATE);
+ strncpy_s(config.name, sizeof(config.name), "DedicatedServer", _TRUNCATE);
+ config.maxPlayers = MINECRAFT_NET_MAX_PLAYERS;
+ config.worldSize = e_worldSize_Classic;
+ config.worldSizeChunks = LEVEL_WIDTH_CLASSIC;
+ config.worldHellScale = HELL_LEVEL_SCALE_CLASSIC;
+ config.seed = 0;
+ config.logLevel = ServerRuntime::eServerLogLevel_Info;
+ config.hasSeed = false;
+ config.showHelp = false;
+
+ SetConsoleCtrlHandler(ConsoleCtrlHandlerProc, TRUE);
+ SetExeWorkingDirectory();
+
+ // Load base settings from server.properties, then override with CLI values when provided
+ ServerPropertiesConfig serverProperties = LoadServerPropertiesConfig();
+ ApplyServerPropertiesToDedicatedConfig(serverProperties, &config);
+
+ if (!ParseCommandLine(argc, argv, &config))
+ {
+ PrintUsage();
+ return 1;
+ }
+ if (config.showHelp)
+ {
+ PrintUsage();
+ return 0;
+ }
+
+ SetServerLogLevel(config.logLevel);
+ LogStartupStep("initializing process state");
+ AccessShutdownGuard accessShutdownGuard;
+
+ g_iScreenWidth = 1280;
+ g_iScreenHeight = 720;
+
+ strncpy_s(g_Win64Username, sizeof(g_Win64Username), config.name, _TRUNCATE);
+ MultiByteToWideChar(CP_ACP, 0, g_Win64Username, -1, g_Win64UsernameW, 17);
+
+ g_Win64MultiplayerHost = true;
+ g_Win64MultiplayerJoin = false;
+ g_Win64MultiplayerPort = config.port;
+ strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), config.bindIP, _TRUNCATE);
+ g_Win64DedicatedServer = true;
+ g_Win64DedicatedServerPort = config.port;
+ strncpy_s(g_Win64DedicatedServerBindIP, sizeof(g_Win64DedicatedServerBindIP), config.bindIP, _TRUNCATE);
+ g_Win64DedicatedServerLanAdvertise = serverProperties.lanAdvertise;
+ LogStartupStep("initializing server log manager");
+ ServerRuntime::ServerLogManager::Initialize();
+ LogStartupStep("initializing dedicated access control");
+ if (!ServerRuntime::Access::Initialize(".", serverProperties.whiteListEnabled))
+ {
+ LogError("startup", "Failed to initialize dedicated server access control.");
+ return 2;
+ }
+ accessShutdownGuard.Activate();
+ LogInfof("startup", "LAN advertise: %s", serverProperties.lanAdvertise ? "enabled" : "disabled");
+ LogInfof("startup", "Whitelist: %s", serverProperties.whiteListEnabled ? "enabled" : "disabled");
+ LogInfof("startup", "Spawn protection radius: %d", serverProperties.spawnProtectionRadius);
+#ifdef _LARGE_WORLDS
+ LogInfof(
+ "startup",
+ "World size preset: %d (xz=%d, hell-scale=%d)",
+ config.worldSize,
+ config.worldSizeChunks,
+ config.worldHellScale);
+#endif
+
+ LogStartupStep("registering hidden window class");
+ HINSTANCE hInstance = GetModuleHandle(NULL);
+ MyRegisterClass(hInstance);
+
+ LogStartupStep("creating hidden window");
+ if (!InitInstance(hInstance, SW_HIDE))
+ {
+ LogError("startup", "Failed to create window instance.");
+
+ return 2;
+ }
+ ShowWindow(g_hWnd, SW_HIDE);
+
+ LogStartupStep("initializing graphics device wrappers");
+ if (FAILED(InitDevice()))
+ {
+ LogError("startup", "Failed to initialize D3D device.");
+ CleanupDevice();
+
+ return 2;
+ }
+
+ LogStartupStep("loading media/string tables");
+ app.loadMediaArchive();
+ RenderManager.Initialise(g_pd3dDevice, g_pSwapChain);
+ app.loadStringTable();
+ ui.init(g_pd3dDevice, g_pImmediateContext, g_pRenderTargetView, g_pDepthStencilView, g_iScreenWidth, g_iScreenHeight);
+
+ InputManager.Initialise(1, 3, MINECRAFT_ACTION_MAX, ACTION_MAX_MENU);
+ g_KBMInput.Init();
+ DefineActions();
+ InputManager.SetJoypadMapVal(0, 0);
+ InputManager.SetKeyRepeatRate(0.3f, 0.2f);
+
+ ProfileManager.Initialise(
+ TITLEID_MINECRAFT,
+ app.m_dwOfferID,
+ PROFILE_VERSION_10,
+ kProfileValueCount,
+ kProfileSettingCount,
+ dwProfileSettingsA,
+ app.GAME_DEFINED_PROFILE_DATA_BYTES * XUSER_MAX_COUNT,
+ &app.uiGameDefinedDataChangedBitmask);
+ ProfileManager.SetDefaultOptionsCallback(&CConsoleMinecraftApp::DefaultOptionsCallback, (LPVOID)&app);
+ ProfileManager.SetDebugFullOverride(true);
+
+ LogStartupStep("initializing network manager");
+ g_NetworkManager.Initialise();
+
+ for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i)
+ {
+ IQNet::m_player[i].m_smallId = (BYTE)i;
+ IQNet::m_player[i].m_isRemote = false;
+ IQNet::m_player[i].m_isHostPlayer = (i == 0);
+ swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Player%d", i);
+ }
+ wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW);
+ WinsockNetLayer::Initialize();
+
+ Tesselator::CreateNewThreadStorage(1024 * 1024);
+ AABB::CreateNewThreadStorage();
+ Vec3::CreateNewThreadStorage();
+ IntCache::CreateNewThreadStorage();
+ Compression::CreateNewThreadStorage();
+ OldChunkStorage::CreateNewThreadStorage();
+ Level::enableLightingCache();
+ Tile::CreateNewThreadStorage();
+
+ LogStartupStep("creating Minecraft singleton");
+ Minecraft::main();
+ Minecraft *minecraft = Minecraft::GetInstance();
+ if (minecraft == NULL)
+ {
+ LogError("startup", "Minecraft initialization failed.");
+ CleanupDevice();
+
+ return 3;
+ }
+
+ app.InitGameSettings();
+
+ MinecraftServer::resetFlags();
+ app.SetTutorialMode(false);
+ app.SetCorruptSaveDeleted(false);
+ app.SetGameHostOption(eGameHostOption_Difficulty, serverProperties.difficulty);
+ app.SetGameHostOption(eGameHostOption_FriendsOfFriends, serverProperties.friendsOfFriends ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_Gamertags, serverProperties.gamertags ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_BedrockFog, serverProperties.bedrockFog ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_GameType, serverProperties.gameMode);
+ app.SetGameHostOption(eGameHostOption_LevelType, serverProperties.levelTypeFlat ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_Structures, serverProperties.generateStructures ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_BonusChest, serverProperties.bonusChest ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_PvP, serverProperties.pvp ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_TrustPlayers, serverProperties.trustPlayers ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_FireSpreads, serverProperties.fireSpreads ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_TNT, serverProperties.tnt ? 1 : 0);
+ app.SetGameHostOption(
+ eGameHostOption_CheatsEnabled,
+ (serverProperties.hostCanFly || serverProperties.hostCanChangeHunger || serverProperties.hostCanBeInvisible) ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_HostCanFly, serverProperties.hostCanFly ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_HostCanChangeHunger, serverProperties.hostCanChangeHunger ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_HostCanBeInvisible, serverProperties.hostCanBeInvisible ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_DisableSaving, serverProperties.disableSaving ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_MobGriefing, serverProperties.mobGriefing ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_KeepInventory, serverProperties.keepInventory ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_DoMobSpawning, serverProperties.doMobSpawning ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_DoMobLoot, serverProperties.doMobLoot ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_DoTileDrops, serverProperties.doTileDrops ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_NaturalRegeneration, serverProperties.naturalRegeneration ? 1 : 0);
+ app.SetGameHostOption(eGameHostOption_DoDaylightCycle, serverProperties.doDaylightCycle ? 1 : 0);
+#ifdef _LARGE_WORLDS
+ app.SetGameHostOption(eGameHostOption_WorldSize, serverProperties.worldSize);
+ // Apply desired target size for loading existing worlds.
+ // Expansion happens only when target size is larger than current level size.
+ app.SetGameNewWorldSize(serverProperties.worldSizeChunks, true);
+ app.SetGameNewHellScale(serverProperties.worldHellScale);
+#endif
+
+ StorageManager.SetSaveDisabled(serverProperties.disableSaving);
+ // Read world name and fixed save-id from server.properties
+ // Delegate load-vs-create decision to WorldManager
+ std::wstring targetWorldName = serverProperties.worldName;
+ if (targetWorldName.empty())
+ {
+ targetWorldName = L"world"; // Default world name
+ }
+ WorldBootstrapResult worldBootstrap = BootstrapWorldForServer(serverProperties, kServerActionPad, &TickCoreSystems);
+ if (worldBootstrap.status == eWorldBootstrap_Loaded)
+ {
+ const std::string &loadedSaveFilename = worldBootstrap.resolvedSaveId;
+ if (!loadedSaveFilename.empty() && _stricmp(loadedSaveFilename.c_str(), serverProperties.worldSaveId.c_str()) != 0)
+ {
+ // Persist the actually loaded save-id back to config
+ // Keep lookup keys aligned for next startup
+ LogWorldIO("updating level-id to loaded save filename");
+ serverProperties.worldSaveId = loadedSaveFilename;
+ if (!SaveServerPropertiesConfig(serverProperties))
+ {
+ LogWorldIO("failed to persist updated level-id");
+ }
+ }
+ }
+ else if (worldBootstrap.status == eWorldBootstrap_Failed)
+ {
+ LogErrorf("world-io", "Failed to load configured world \"%s\".", WideToUtf8(targetWorldName).c_str());
+ WinsockNetLayer::Shutdown();
+ g_NetworkManager.Terminate();
+ CleanupDevice();
+
+ return 4;
+ }
+
+ NetworkGameInitData *param = new NetworkGameInitData();
+ if (config.hasSeed)
+ {
+ param->seed = config.seed;
+ }
+ else
+ {
+ param->seed = (new Random())->nextLong();
+ }
+#ifdef _LARGE_WORLDS
+ param->xzSize = (unsigned int)config.worldSizeChunks;
+ param->hellScale = (unsigned char)config.worldHellScale;
+#endif
+ param->saveData = worldBootstrap.saveData;
+ param->settings = app.GetGameHostOption(eGameHostOption_All);
+ param->dedicatedNoLocalHostPlayer = true;
+
+ LogStartupStep("starting hosted network game thread");
+ g_NetworkManager.HostGame(0, true, false, (unsigned char)config.maxPlayers, 0);
+ g_NetworkManager.FakeLocalPlayerJoined();
+
+ C4JThread *startThread = new C4JThread(&CGameNetworkManager::RunNetworkGameThreadProc, (LPVOID)param, "RunNetworkGame");
+ startThread->Run();
+
+ while (startThread->isRunning() && !IsShutdownRequested())
+ {
+ TickCoreSystems();
+ Sleep(10);
+ }
+
+ startThread->WaitForCompletion(INFINITE);
+ int startupResult = startThread->GetExitCode();
+ delete startThread;
+
+ if (startupResult != 0)
+ {
+ LogErrorf("startup", "Failed to start dedicated server (code %d).", startupResult);
+ WinsockNetLayer::Shutdown();
+ g_NetworkManager.Terminate();
+ CleanupDevice();
+
+ return 4;
+ }
+
+ LogStartupStep("server startup complete");
+ LogInfof("startup", "Dedicated server listening on %s:%d", g_Win64MultiplayerIP, g_Win64MultiplayerPort);
+ if (worldBootstrap.status == eWorldBootstrap_CreatedNew && !IsShutdownRequested() && !app.m_bShutdown)
+ {
+ // Windows64 suppresses saveToDisc right after new world creation
+ // Dedicated Server explicitly runs the initial save here
+ LogWorldIO("requesting initial save for newly created world");
+ WaitForWorldActionIdle(kServerActionPad, 5000, &TickCoreSystems, &HandleXuiActions);
+ app.SetXuiServerAction(kServerActionPad, eXuiServerAction_AutoSaveGame);
+ if (!WaitForWorldActionIdle(kServerActionPad, 30000, &TickCoreSystems, &HandleXuiActions))
+ {
+ LogWorldIO("initial save timed out");
+ LogWarn("world-io", "Timed out waiting for initial save action to finish.");
+ }
+ else
+ {
+ LogWorldIO("initial save completed");
+ }
+ }
+ DWORD autosaveIntervalMs = kDefaultAutosaveIntervalMs;
+ if (serverProperties.autosaveIntervalSeconds > 0)
+ {
+ autosaveIntervalMs = (DWORD)(serverProperties.autosaveIntervalSeconds * 1000);
+ }
+ DWORD nextAutosaveTick = GetTickCount() + autosaveIntervalMs;
+ bool autosaveRequested = false;
+ ServerRuntime::ServerCli serverCli;
+ serverCli.Start();
+
+ while (!IsShutdownRequested() && !app.m_bShutdown)
+ {
+ TickCoreSystems();
+ HandleXuiActions();
+ serverCli.Poll();
+
+ if (IsShutdownRequested() || app.m_bShutdown)
+ {
+ break;
+ }
+
+ if (autosaveRequested && app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle)
+ {
+ LogWorldIO("autosave completed");
+ autosaveRequested = false;
+ }
+
+ if (MinecraftServer::serverHalted())
+ {
+ break;
+ }
+
+ DWORD now = GetTickCount();
+ if ((LONG)(now - nextAutosaveTick) >= 0)
+ {
+ if (app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle)
+ {
+ LogWorldIO("requesting autosave");
+ app.SetXuiServerAction(kServerActionPad, eXuiServerAction_AutoSaveGame);
+ autosaveRequested = true;
+ }
+ nextAutosaveTick = now + autosaveIntervalMs;
+ }
+
+ Sleep(10);
+ }
+ serverCli.Stop();
+ app.m_bShutdown = true;
+
+ LogInfof("shutdown", "Dedicated server stopped");
+ MinecraftServer *server = MinecraftServer::getInstance();
+ if (server != NULL)
+ {
+ server->setSaveOnExit(true);
+ }
+ if (server != NULL)
+ {
+ LogWorldIO("requesting save before shutdown");
+ LogWorldIO("using saveOnExit for shutdown");
+ }
+
+ MinecraftServer::HaltServer();
+
+ if (g_NetworkManager.ServerStoppedValid())
+ {
+ C4JThread waitThread(&WaitForServerStoppedThreadProc, NULL, "WaitServerStopped");
+ waitThread.Run();
+ waitThread.WaitForCompletion(INFINITE);
+ }
+
+ LogInfof("shutdown", "Cleaning up and exiting.");
+ WinsockNetLayer::Shutdown();
+ LogDebugf("shutdown", "Network layer shutdown complete.");
+ g_NetworkManager.Terminate();
+ LogDebugf("shutdown", "Network manager terminated.");
+ ServerRuntime::ServerLogManager::Shutdown();
+ CleanupDevice();
+
+
+ return 0;
+}
+
diff --git a/Minecraft.Server/Windows64/postbuild_server.ps1 b/Minecraft.Server/Windows64/postbuild_server.ps1
new file mode 100644
index 00000000..27451ba2
--- /dev/null
+++ b/Minecraft.Server/Windows64/postbuild_server.ps1
@@ -0,0 +1,65 @@
+param(
+ [string]$OutDir,
+ [string]$ProjectRoot,
+ [string]$Configuration
+)
+
+if ([string]::IsNullOrWhiteSpace($OutDir)) {
+ throw "OutDir is required."
+}
+
+if ([string]::IsNullOrWhiteSpace($ProjectRoot)) {
+ $ProjectRoot = Resolve-Path (Join-Path $PSScriptRoot "..\\..")
+}
+
+if ([string]::IsNullOrWhiteSpace($Configuration)) {
+ $Configuration = "Debug"
+}
+
+$OutDir = [System.IO.Path]::GetFullPath($OutDir)
+$ProjectRoot = [System.IO.Path]::GetFullPath($ProjectRoot)
+$ClientRoot = Join-Path $ProjectRoot "Minecraft.Client"
+
+Write-Host "Server post-build started. OutDir: $OutDir"
+
+function Ensure-Dir([string]$path) {
+ if (-not (Test-Path $path)) {
+ New-Item -ItemType Directory -Path $path -Force | Out-Null
+ }
+}
+
+function Copy-Tree-IfExists([string]$src, [string]$dst) {
+ if (Test-Path $src) {
+ Ensure-Dir $dst
+ xcopy /q /y /i /s /e /d "$src" "$dst" 2>$null | Out-Null
+ }
+}
+
+function Copy-File-IfExists([string]$src, [string]$dst) {
+ if (Test-Path $src) {
+ $dstDir = Split-Path -Parent $dst
+ Ensure-Dir $dstDir
+ xcopy /q /y /d "$src" "$dstDir" 2>$null | Out-Null
+ }
+}
+
+function Copy-FirstExisting([string[]]$candidates, [string]$dstFile) {
+ foreach ($candidate in $candidates) {
+ if (Test-Path $candidate) {
+ Copy-File-IfExists $candidate $dstFile
+ return
+ }
+ }
+}
+
+# Dedicated server only needs core resources for current startup path.
+Copy-File-IfExists (Join-Path $ClientRoot "Common\\Media\\MediaWindows64.arc") (Join-Path $OutDir "Common\\Media\\MediaWindows64.arc")
+Copy-Tree-IfExists (Join-Path $ClientRoot "Common\\res") (Join-Path $OutDir "Common\\res")
+Copy-Tree-IfExists (Join-Path $ClientRoot "Windows64\\GameHDD") (Join-Path $OutDir "Windows64\\GameHDD")
+
+# Runtime DLLs.
+Copy-FirstExisting @(
+ (Join-Path $ClientRoot "Windows64\\Iggy\\lib\\redist64\\iggy_w64.dll"),
+ (Join-Path $ProjectRoot ("x64\\{0}\\iggy_w64.dll" -f $Configuration))
+) (Join-Path $OutDir "iggy_w64.dll")
+
diff --git a/Minecraft.Server/WorldManager.cpp b/Minecraft.Server/WorldManager.cpp
new file mode 100644
index 00000000..b9ec3dd9
--- /dev/null
+++ b/Minecraft.Server/WorldManager.cpp
@@ -0,0 +1,641 @@
+#include "stdafx.h"
+
+#include "WorldManager.h"
+
+#include "Minecraft.h"
+#include "MinecraftServer.h"
+#include "ServerLogger.h"
+#include "Common\\StringUtils.h"
+
+#include <stdio.h>
+#include <string.h>
+
+namespace ServerRuntime
+{
+using StringUtils::Utf8ToWide;
+using StringUtils::WideToUtf8;
+
+enum EWorldSaveLoadResult
+{
+ eWorldSaveLoad_Loaded,
+ eWorldSaveLoad_NotFound,
+ eWorldSaveLoad_Failed
+};
+
+struct SaveInfoQueryContext
+{
+ bool done;
+ bool success;
+ SAVE_DETAILS *details;
+
+ SaveInfoQueryContext()
+ : done(false)
+ , success(false)
+ , details(NULL)
+ {
+ }
+};
+
+struct SaveDataLoadContext
+{
+ bool done;
+ bool isCorrupt;
+ bool isOwner;
+
+ SaveDataLoadContext()
+ : done(false)
+ , isCorrupt(true)
+ , isOwner(false)
+ {
+ }
+};
+
+/**
+ * **Apply Save ID To StorageManager**
+ *
+ * Applies the configured save destination ID (`level-id`) to `StorageManager`
+ * - Re-applies the same ID at startup and before save to avoid destination drift
+ * - Ignores empty values as invalid
+ * - For some reason, a date-based world file occasionally appears after a debug build, but the cause is unknown.
+ * 保存先IDの適用処理
+ *
+ * @param saveFilename Normalized save destination ID
+ */
+static void SetStorageSaveUniqueFilename(const std::string &saveFilename)
+{
+ if (saveFilename.empty())
+ {
+ return;
+ }
+
+ char filenameBuffer[64] = {};
+ strncpy_s(filenameBuffer, sizeof(filenameBuffer), saveFilename.c_str(), _TRUNCATE);
+ StorageManager.SetSaveUniqueFilename(filenameBuffer);
+}
+
+static void LogSaveFilename(const char *prefix, const std::string &saveFilename)
+{
+ LogInfof("world-io", "%s: %s", (prefix != NULL) ? prefix : "save-filename", saveFilename.c_str());
+}
+
+/**
+ * Verifies a directory exists and creates it when missing
+ * - Treats an existing non-directory path as failure
+ * - Returns whether the directory had to be created
+ * ディレクトリ存在保証の補助処理
+ */
+static bool EnsureDirectoryExists(const std::wstring &directoryPath, bool *outCreated)
+{
+ if (outCreated != NULL)
+ {
+ *outCreated = false;
+ }
+ if (directoryPath.empty())
+ {
+ return false;
+ }
+
+ DWORD attrs = GetFileAttributesW(directoryPath.c_str());
+ if (attrs != INVALID_FILE_ATTRIBUTES)
+ {
+ if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ {
+ return true;
+ }
+
+ LogErrorf("world-io", "path exists but is not a directory: %s", WideToUtf8(directoryPath).c_str());
+ return false;
+ }
+
+ if (CreateDirectoryW(directoryPath.c_str(), NULL))
+ {
+ if (outCreated != NULL)
+ {
+ *outCreated = true;
+ }
+ return true;
+ }
+
+ DWORD error = GetLastError();
+ if (error == ERROR_ALREADY_EXISTS)
+ {
+ attrs = GetFileAttributesW(directoryPath.c_str());
+ if (attrs != INVALID_FILE_ATTRIBUTES && ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0))
+ {
+ return true;
+ }
+ }
+
+ LogErrorf(
+ "world-io",
+ "failed to create directory %s (error=%lu)",
+ WideToUtf8(directoryPath).c_str(),
+ (unsigned long)error);
+ return false;
+}
+
+/**
+ * Prepares the save root used by the Windows64 storage layout
+ * - Creates `Windows64` first because `CreateDirectoryW` is not recursive
+ * - Creates `Windows64\\GameHDD` when missing before world bootstrap starts
+ * Windows64用保存先ディレクトリの存在保証
+ */
+static bool EnsureGameHddRootExists()
+{
+ bool windows64Created = false;
+ if (!EnsureDirectoryExists(L"Windows64", &windows64Created))
+ {
+ return false;
+ }
+
+ bool gameHddCreated = false;
+ if (!EnsureDirectoryExists(L"Windows64\\GameHDD", &gameHddCreated))
+ {
+ return false;
+ }
+
+ if (windows64Created || gameHddCreated)
+ {
+ LogWorldIO("created missing Windows64\\GameHDD storage directories");
+ }
+
+ return true;
+}
+
+static void LogEnumeratedSaveInfo(int index, const SAVE_INFO &saveInfo)
+{
+ std::wstring title = Utf8ToWide(saveInfo.UTF8SaveTitle);
+ std::wstring filename = Utf8ToWide(saveInfo.UTF8SaveFilename);
+ std::string titleUtf8 = WideToUtf8(title);
+ std::string filenameUtf8 = WideToUtf8(filename);
+
+ char logLine[512] = {};
+ sprintf_s(
+ logLine,
+ sizeof(logLine),
+ "save[%d] title=\"%s\" filename=\"%s\"",
+ index,
+ titleUtf8.c_str(),
+ filenameUtf8.c_str());
+ LogDebug("world-io", logLine);
+}
+
+/**
+ * **Save List Callback**
+ *
+ * Captures async save-list results into `SaveInfoQueryContext` and marks completion for the waiter
+ * セーブ一覧取得の完了通知
+ */
+static int GetSavesInfoCallbackProc(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, const bool bRes)
+{
+ SaveInfoQueryContext *context = (SaveInfoQueryContext *)lpParam;
+ if (context != NULL)
+ {
+ context->details = pSaveDetails;
+ context->success = bRes;
+ context->done = true;
+ }
+ return 0;
+}
+
+/**
+ * **Save Data Load Callback**
+ *
+ * Writes load results such as corruption status into `SaveDataLoadContext`
+ * セーブ読み込み結果の反映
+ */
+static int LoadSaveDataCallbackProc(LPVOID lpParam, const bool bIsCorrupt, const bool bIsOwner)
+{
+ SaveDataLoadContext *context = (SaveDataLoadContext *)lpParam;
+ if (context != NULL)
+ {
+ context->isCorrupt = bIsCorrupt;
+ context->isOwner = bIsOwner;
+ context->done = true;
+ }
+ return 0;
+}
+
+/**
+ * **Wait For Save List Completion**
+ *
+ * Waits until save-list retrieval completes
+ * - Prefers callback completion as the primary signal
+ * - Also falls back to polling because some environments populate `ReturnSavesInfo()` before callback
+ * セーブ一覧待機の補助処理
+ *
+ * @return `true` when completion is detected
+ */
+static bool WaitForSaveInfoResult(SaveInfoQueryContext *context, DWORD timeoutMs, WorldManagerTickProc tickProc)
+{
+ DWORD start = GetTickCount();
+ while ((GetTickCount() - start) < timeoutMs)
+ {
+ if (context->done)
+ {
+ return true;
+ }
+
+ if (context->details == NULL)
+ {
+ // Some implementations fill ReturnSavesInfo before the callback
+ // Keep polling as a fallback instead of relying only on callback completion
+ SAVE_DETAILS *details = StorageManager.ReturnSavesInfo();
+ if (details != NULL)
+ {
+ context->details = details;
+ context->success = true;
+ context->done = true;
+ return true;
+ }
+ }
+
+ if (tickProc != NULL)
+ {
+ tickProc();
+ }
+ Sleep(10);
+ }
+
+ return context->done;
+}
+
+/**
+ * **Wait For Save Data Load Completion**
+ *
+ * Waits for the save-data load callback to complete
+ * セーブ本体の読み込み待機
+ *
+ * @return `true` when callback is reached, `false` on timeout
+ */
+static bool WaitForSaveLoadResult(SaveDataLoadContext *context, DWORD timeoutMs, WorldManagerTickProc tickProc)
+{
+ DWORD start = GetTickCount();
+ while ((GetTickCount() - start) < timeoutMs)
+ {
+ if (context->done)
+ {
+ return true;
+ }
+
+ if (tickProc != NULL)
+ {
+ tickProc();
+ }
+ Sleep(10);
+ }
+
+ return context->done;
+}
+
+/**
+ * **Match SAVE_INFO By World Name**
+ *
+ * Compares both save title and save filename against the target world name
+ * ワールド名一致判定
+ */
+static bool SaveInfoMatchesWorldName(const SAVE_INFO &saveInfo, const std::wstring &targetWorldName)
+{
+ if (targetWorldName.empty())
+ {
+ return false;
+ }
+
+ std::wstring saveTitle = Utf8ToWide(saveInfo.UTF8SaveTitle);
+ std::wstring saveFilename = Utf8ToWide(saveInfo.UTF8SaveFilename);
+
+ if (!saveTitle.empty() && (_wcsicmp(saveTitle.c_str(), targetWorldName.c_str()) == 0))
+ {
+ return true;
+ }
+ if (!saveFilename.empty() && (_wcsicmp(saveFilename.c_str(), targetWorldName.c_str()) == 0))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * **Match SAVE_INFO By Save Filename**
+ *
+ * Checks whether `SAVE_INFO` matches by save destination ID (`UTF8SaveFilename`)
+ * 保存先ID一致判定
+ */
+static bool SaveInfoMatchesSaveFilename(const SAVE_INFO &saveInfo, const std::string &targetSaveFilename)
+{
+ if (targetSaveFilename.empty() || saveInfo.UTF8SaveFilename[0] == 0)
+ {
+ return false;
+ }
+
+ return (_stricmp(saveInfo.UTF8SaveFilename, targetSaveFilename.c_str()) == 0);
+}
+
+/**
+ * **Apply World Identity To Storage**
+ *
+ * Applies world identity (`level-name` + `level-id`) to storage
+ * - Always sets both display name and ID to avoid partial configuration
+ * - Helps prevent unintended new save destinations across environment differences
+ * 保存先と表示名の同期処理
+ */
+static void ApplyWorldStorageTarget(const std::wstring &worldName, const std::string &saveId)
+{
+ // Set both title (display name) and save ID (actual folder name) explicitly
+ // Setting only one side can create unexpected new save targets in some environments
+ StorageManager.SetSaveTitle(worldName.c_str());
+ SetStorageSaveUniqueFilename(saveId);
+}
+
+/**
+ * **Prepare World Save Data For Startup**
+ *
+ * Searches for a save matching the target world and extracts startup payload when found
+ * Match priority:
+ * 1. Exact match by `level-id` (`UTF8SaveFilename`)
+ * 2. Fallback match by `level-name` against title or filename
+ * ワールド一致セーブの探索処理
+ *
+ * @return
+ * - `eWorldSaveLoad_Loaded`: Existing save loaded successfully
+ * - `eWorldSaveLoad_NotFound`: No matching save found
+ * - `eWorldSaveLoad_Failed`: API failure, corruption, or invalid data
+ */
+static EWorldSaveLoadResult PrepareWorldSaveData(
+ const std::wstring &targetWorldName,
+ const std::string &targetSaveFilename,
+ int actionPad,
+ WorldManagerTickProc tickProc,
+ LoadSaveDataThreadParam **outSaveData,
+ std::string *outResolvedSaveFilename)
+{
+ if (outSaveData == NULL)
+ {
+ return eWorldSaveLoad_Failed;
+ }
+ *outSaveData = NULL;
+ if (outResolvedSaveFilename != NULL)
+ {
+ outResolvedSaveFilename->clear();
+ }
+
+ LogWorldIO("enumerating saves for configured world");
+ StorageManager.ClearSavesInfo();
+
+ SaveInfoQueryContext infoContext;
+ int infoState = StorageManager.GetSavesInfo(actionPad, &GetSavesInfoCallbackProc, &infoContext, "save");
+ if (infoState == C4JStorage::ESaveGame_Idle)
+ {
+ infoContext.done = true;
+ infoContext.success = true;
+ infoContext.details = StorageManager.ReturnSavesInfo();
+ }
+ else if (infoState != C4JStorage::ESaveGame_GetSavesInfo)
+ {
+ LogWorldIO("GetSavesInfo failed to start");
+ return eWorldSaveLoad_Failed;
+ }
+
+ if (!WaitForSaveInfoResult(&infoContext, 10000, tickProc))
+ {
+ LogWorldIO("timed out waiting for save list");
+ return eWorldSaveLoad_Failed;
+ }
+
+ if (infoContext.details == NULL)
+ {
+ infoContext.details = StorageManager.ReturnSavesInfo();
+ }
+ if (infoContext.details == NULL)
+ {
+ LogWorldIO("failed to retrieve save list");
+ return eWorldSaveLoad_Failed;
+ }
+
+ int matchedIndex = -1;
+ if (!targetSaveFilename.empty())
+ {
+ // 1) If save ID is provided, search by it first
+ // This is the most stable way to reuse the same world target
+ for (int i = 0; i < infoContext.details->iSaveC; ++i)
+ {
+ LogEnumeratedSaveInfo(i, infoContext.details->SaveInfoA[i]);
+ if (SaveInfoMatchesSaveFilename(infoContext.details->SaveInfoA[i], targetSaveFilename))
+ {
+ matchedIndex = i;
+ break;
+ }
+ }
+ }
+
+ if (matchedIndex < 0 && targetSaveFilename.empty())
+ {
+ for (int i = 0; i < infoContext.details->iSaveC; ++i)
+ {
+ LogEnumeratedSaveInfo(i, infoContext.details->SaveInfoA[i]);
+ }
+ }
+
+ for (int i = 0; i < infoContext.details->iSaveC; ++i)
+ {
+ // 2) If no save matched by ID, try compatibility fallback
+ // Match worldName against save title or save filename
+ if (matchedIndex >= 0)
+ {
+ break;
+ }
+ if (SaveInfoMatchesWorldName(infoContext.details->SaveInfoA[i], targetWorldName))
+ {
+ matchedIndex = i;
+ break;
+ }
+ }
+
+ if (matchedIndex < 0)
+ {
+ LogWorldIO("no save matched configured world name");
+ return eWorldSaveLoad_NotFound;
+ }
+
+ std::wstring matchedTitle = Utf8ToWide(infoContext.details->SaveInfoA[matchedIndex].UTF8SaveTitle);
+ if (matchedTitle.empty())
+ {
+ matchedTitle = targetWorldName;
+ }
+ LogWorldName("matched save title", matchedTitle);
+ SAVE_INFO *matchedSaveInfo = &infoContext.details->SaveInfoA[matchedIndex];
+ std::wstring matchedFilename = Utf8ToWide(matchedSaveInfo->UTF8SaveFilename);
+ if (!matchedFilename.empty())
+ {
+ LogWorldName("matched save filename", matchedFilename);
+ }
+
+ ApplyWorldStorageTarget(targetWorldName, targetSaveFilename);
+
+ std::string resolvedSaveFilename;
+ if (matchedSaveInfo->UTF8SaveFilename[0] != 0)
+ {
+ // Prefer the save ID that was actually matched, then keep using it for future saves
+ resolvedSaveFilename = matchedSaveInfo->UTF8SaveFilename;
+ SetStorageSaveUniqueFilename(resolvedSaveFilename);
+ }
+ else if (!targetSaveFilename.empty())
+ {
+ resolvedSaveFilename = targetSaveFilename;
+ }
+
+ if (outResolvedSaveFilename != NULL)
+ {
+ *outResolvedSaveFilename = resolvedSaveFilename;
+ }
+
+ SaveDataLoadContext loadContext;
+ int loadState = StorageManager.LoadSaveData(matchedSaveInfo, &LoadSaveDataCallbackProc, &loadContext);
+ if (loadState != C4JStorage::ESaveGame_Load && loadState != C4JStorage::ESaveGame_Idle)
+ {
+ LogWorldIO("LoadSaveData failed to start");
+ return eWorldSaveLoad_Failed;
+ }
+
+ if (loadState == C4JStorage::ESaveGame_Load)
+ {
+ if (!WaitForSaveLoadResult(&loadContext, 15000, tickProc))
+ {
+ LogWorldIO("timed out waiting for save data load");
+ return eWorldSaveLoad_Failed;
+ }
+ if (loadContext.isCorrupt)
+ {
+ LogWorldIO("target save is corrupt; aborting load");
+ return eWorldSaveLoad_Failed;
+ }
+ }
+
+ unsigned int saveSize = StorageManager.GetSaveSize();
+ if (saveSize == 0)
+ {
+ // Treat zero-byte payload as failure even when load API reports success
+ LogWorldIO("loaded save has zero size");
+ return eWorldSaveLoad_Failed;
+ }
+
+ byteArray loadedSaveData(saveSize, false);
+ unsigned int loadedSize = saveSize;
+ StorageManager.GetSaveData(loadedSaveData.data, &loadedSize);
+ if (loadedSize == 0)
+ {
+ LogWorldIO("failed to copy loaded save data from storage manager");
+ return eWorldSaveLoad_Failed;
+ }
+
+ *outSaveData = new LoadSaveDataThreadParam(loadedSaveData.data, loadedSize, matchedTitle);
+ LogWorldIO("prepared save data payload for server startup");
+ return eWorldSaveLoad_Loaded;
+}
+
+/**
+ * **Bootstrap World State For Server Startup**
+ *
+ * Determines final world startup state
+ * - Returns loaded save data when an existing save is found
+ * - Prepares a new world context when not found
+ * - Returns `Failed` when startup should be aborted
+ * サーバー起動時のワールド確定処理
+ */
+WorldBootstrapResult BootstrapWorldForServer(
+ const ServerPropertiesConfig &config,
+ int actionPad,
+ WorldManagerTickProc tickProc)
+{
+ WorldBootstrapResult result;
+ if (!EnsureGameHddRootExists())
+ {
+ LogWorldIO("failed to prepare Windows64\\GameHDD storage root");
+ return result;
+ }
+
+ std::wstring targetWorldName = config.worldName;
+ std::string targetSaveFilename = config.worldSaveId;
+ if (targetWorldName.empty())
+ {
+ targetWorldName = L"world";
+ }
+
+ LogWorldName("configured level-name", targetWorldName);
+ if (!targetSaveFilename.empty())
+ {
+ LogSaveFilename("configured level-id", targetSaveFilename);
+ }
+
+ ApplyWorldStorageTarget(targetWorldName, targetSaveFilename);
+
+ std::string loadedSaveFilename;
+ EWorldSaveLoadResult worldLoadResult = PrepareWorldSaveData(
+ targetWorldName,
+ targetSaveFilename,
+ actionPad,
+ tickProc,
+ &result.saveData,
+ &loadedSaveFilename);
+ if (worldLoadResult == eWorldSaveLoad_Loaded)
+ {
+ result.status = eWorldBootstrap_Loaded;
+ result.resolvedSaveId = loadedSaveFilename;
+ LogStartupStep("loading configured world from save data");
+ }
+ else if (worldLoadResult == eWorldSaveLoad_NotFound)
+ {
+ // Create a new context only when no matching save exists
+ // Fix saveId here so the next startup writes to the same location
+ result.status = eWorldBootstrap_CreatedNew;
+ result.resolvedSaveId = targetSaveFilename;
+ LogStartupStep("configured world not found; creating new world");
+ LogWorldIO("creating new world save context");
+ StorageManager.ResetSaveData();
+ ApplyWorldStorageTarget(targetWorldName, targetSaveFilename);
+ }
+ else
+ {
+ result.status = eWorldBootstrap_Failed;
+ }
+
+ return result;
+}
+
+/**
+ * **Wait Until Server XUI Action Is Idle**
+ *
+ * Keeps tick/handle running during save action so async processing does not stall
+ * XUIアクション待機中の進行維持処理
+ */
+bool WaitForWorldActionIdle(
+ int actionPad,
+ DWORD timeoutMs,
+ WorldManagerTickProc tickProc,
+ WorldManagerHandleActionsProc handleActionsProc)
+{
+ DWORD start = GetTickCount();
+ while (app.GetXuiServerAction(actionPad) != eXuiServerAction_Idle && !MinecraftServer::serverHalted())
+ {
+ // Keep network and storage progressing while waiting
+ // If this stops, save action itself may stall and time out
+ if (tickProc != NULL)
+ {
+ tickProc();
+ }
+ if (handleActionsProc != NULL)
+ {
+ handleActionsProc();
+ }
+ if ((GetTickCount() - start) >= timeoutMs)
+ {
+ return false;
+ }
+ Sleep(10);
+ }
+
+ return (app.GetXuiServerAction(actionPad) == eXuiServerAction_Idle);
+}
+}
+
diff --git a/Minecraft.Server/WorldManager.h b/Minecraft.Server/WorldManager.h
new file mode 100644
index 00000000..a4a8e77b
--- /dev/null
+++ b/Minecraft.Server/WorldManager.h
@@ -0,0 +1,92 @@
+#pragma once
+
+#include <string>
+#include <windows.h>
+
+#include "ServerProperties.h"
+
+struct _LoadSaveDataThreadParam;
+typedef struct _LoadSaveDataThreadParam LoadSaveDataThreadParam;
+
+namespace ServerRuntime
+{
+ /** Tick callback used while waiting on async storage/network work */
+ typedef void (*WorldManagerTickProc)();
+ /** Optional action handler used while waiting for server actions */
+ typedef void (*WorldManagerHandleActionsProc)();
+
+ /**
+ * **World Bootstrap Status**
+ *
+ * Result type for world startup preparation, either loading an existing world or creating a new one
+ * ワールド起動準備の結果種別
+ */
+ enum EWorldBootstrapStatus
+ {
+ /** Found and loaded an existing world */
+ eWorldBootstrap_Loaded,
+ /** No matching save was found, created a new world context */
+ eWorldBootstrap_CreatedNew,
+ /** Bootstrap failed and server startup should be aborted */
+ eWorldBootstrap_Failed
+ };
+
+ /**
+ * **World Bootstrap Result**
+ *
+ * Output payload returned by world startup preparation
+ * ワールド起動準備の出力データ
+ */
+ struct WorldBootstrapResult
+ {
+ /** Bootstrap status */
+ EWorldBootstrapStatus status;
+ /** Save data used for server initialization, `NULL` when creating a new world */
+ LoadSaveDataThreadParam *saveData;
+ /** Save ID that was actually selected */
+ std::string resolvedSaveId;
+
+ WorldBootstrapResult()
+ : status(eWorldBootstrap_Failed)
+ , saveData(NULL)
+ {
+ }
+ };
+
+ /**
+ * **Bootstrap Target World For Server Startup**
+ *
+ * Resolves whether the target world should be loaded from an existing save or created as new
+ * - Applies `level-name` and `level-id` from `server.properties`
+ * - Loads when a matching save exists
+ * - Creates a new world context only when no save matches
+ * サーバー起動時のロードか新規作成かを確定する
+ *
+ * @param config Normalized `server.properties` values
+ * @param actionPad padId used by async storage APIs
+ * @param tickProc Tick callback run while waiting for async completion
+ * @return Bootstrap result including whether save data was loaded
+ */
+ WorldBootstrapResult BootstrapWorldForServer(
+ const ServerPropertiesConfig &config,
+ int actionPad,
+ WorldManagerTickProc tickProc);
+
+ /**
+ * **Wait Until Server Action Returns To Idle**
+ *
+ * Waits until server action state reaches `Idle`
+ * サーバーアクションの待機処理
+ *
+ * @param actionPad padId to monitor
+ * @param timeoutMs Timeout in milliseconds
+ * @param tickProc Tick callback run inside the wait loop
+ * @param handleActionsProc Optional action handler callback
+ * @return `true` when `Idle` is reached before timeout
+ */
+ bool WaitForWorldActionIdle(
+ int actionPad,
+ DWORD timeoutMs,
+ WorldManagerTickProc tickProc,
+ WorldManagerHandleActionsProc handleActionsProc);
+}
diff --git a/Minecraft.Server/docs/DEVELOPMENT.en.md b/Minecraft.Server/docs/DEVELOPMENT.en.md
new file mode 100644
index 00000000..5348f020
--- /dev/null
+++ b/Minecraft.Server/docs/DEVELOPMENT.en.md
@@ -0,0 +1,284 @@
+# Minecraft.Server Developer Guide (English)
+
+This document is for contributors who are new to `Minecraft.Server` and need a practical map for adding or modifying features safely.
+
+## 1. What This Server Does
+
+`Minecraft.Server` is the dedicated-server executable entry for this codebase.
+
+Core responsibilities:
+- Switch the process working directory to the executable folder before relative file I/O
+- Load, normalize, and repair `server.properties`
+- Initialize dedicated runtime systems, connection logging, and access control
+- Load or create the target world and keep `level-id` aligned with the actual save destination
+- Run the dedicated main loop (network tick, XUI actions, autosave, CLI input)
+- Maintain operator-facing access files such as `banned-players.json` and `banned-ips.json`
+- Perform an initial save for newly created worlds and then shut down safely
+
+## 2. Important Files
+
+### Startup and Runtime
+- `Windows64/ServerMain.cpp`
+ - `PrintUsage()` and `ParseCommandLine()`
+ - `SetExeWorkingDirectory()`
+ - Runtime setup and shutdown flow
+ - Initial save path for newly created worlds
+ - Main loop, autosave scheduler, and CLI polling
+
+### World Selection and Save Load
+- `WorldManager.h`
+- `WorldManager.cpp`
+ - Finds matching save by `level-id` first, then world-name fallback
+ - Applies storage title + save ID consistently
+ - Wait helpers for async storage/server action completion
+
+### Server Properties
+- `ServerProperties.h`
+- `ServerProperties.cpp`
+ - Default values and normalization ranges
+ - Parse/repair/write `server.properties`
+ - Exposes `ServerPropertiesConfig`
+ - `SaveServerPropertiesConfig()` rewrites `level-name`, `level-id`, and `white-list`
+
+### Access Control, Ban, and Whitelist Storage
+- `Access/Access.h`
+- `Access/Access.cpp`
+ - Process-wide access-control facade
+ - Published snapshot model used by console commands and login checks
+- `Access/BanManager.h`
+- `Access/BanManager.cpp`
+ - Reads/writes `banned-players.json` and `banned-ips.json`
+ - Normalizes identifiers and filters expired entries from snapshots
+- `Access/WhitelistManager.h`
+- `Access/WhitelistManager.cpp`
+ - Reads/writes `whitelist.json`
+ - Normalizes XUID-based whitelist entries used by login validation and CLI commands
+
+### Logging and Connection Audit
+- `ServerLogger.h`
+- `ServerLogger.cpp`
+ - Log level parsing
+ - Colored/timestamped console logs
+ - General categories such as `startup`, `world-io`, `console`, `access`, `network`, and `shutdown`
+- `ServerLogManager.h`
+- `ServerLogManager.cpp`
+ - Accepted/rejected TCP connection logs
+ - Login/disconnect audit logs
+ - Remote-IP cache used by `ban-ip <player>`
+
+### Console Command System
+- `Console/ServerCli.cpp` (facade)
+- `Console/ServerCliInput.cpp` (linenoise input thread + completion bridge)
+- `Console/ServerCliParser.cpp` (tokenization, quoted args, completion context)
+- `Console/ServerCliEngine.cpp` (dispatch, completion, helpers)
+- `Console/ServerCliRegistry.cpp` (command registration + lookup)
+- `Console/commands/*` (individual commands)
+
+## 3. End-to-End Startup Flow
+
+Main flow in `Windows64/ServerMain.cpp`:
+1. `SetExeWorkingDirectory()` switches the current directory to the executable folder.
+2. Load and normalize `server.properties` via `LoadServerPropertiesConfig()`.
+3. Copy config into `DedicatedServerConfig`, then apply CLI overrides (`-port`, `-ip`/`-bind`, `-name`, `-maxplayers`, `-seed`, `-loglevel`, `-help`/`--help`/`-h`).
+4. Initialize process state, `ServerLogManager`, and `Access::Initialize(".")`.
+5. Initialize window/device/profile/network/thread-local systems.
+6. Set host/game options from `ServerPropertiesConfig`.
+7. Bootstrap world with `BootstrapWorldForServer(...)`.
+8. If world bootstrap resolves a different normalized save ID, persist it with `SaveServerPropertiesConfig()`.
+9. Start hosted game thread (`RunNetworkGameThreadProc`).
+10. If a brand-new world was created, explicitly request one initial save.
+11. Enter the main loop:
+ - `TickCoreSystems()`
+ - `HandleXuiActions()`
+ - `serverCli.Poll()`
+ - autosave scheduling
+12. On shutdown:
+ - stop CLI input
+ - request save-on-exit / halt server
+ - wait for network shutdown completion
+ - terminate log, access, network, and device systems
+
+## 4. Current Operator Surface
+
+### 4.1 Launch Arguments
+- `-port <1-65535>`
+- `-ip <addr>` or `-bind <addr>`
+- `-name <name>` (runtime max 16 chars)
+- `-maxplayers <1-8>`
+- `-seed <int64>`
+- `-loglevel <debug|info|warn|error>`
+- `-help`, `--help`, `-h`
+
+Notes:
+- CLI overrides affect only the current process.
+- The only values currently written back by the server are `level-name` and `level-id`, and that happens when world bootstrap resolves identity changes.
+
+### 4.2 Built-in Console Commands
+- `help` / `?`
+- `stop`
+- `list`
+- `ban <player> [reason ...]`
+ - currently requires the target player to be online
+- `ban-ip <address|player> [reason ...]`
+ - accepts a literal IPv4/IPv6 address or an online player's current remote IP
+- `pardon <player>`
+- `pardon-ip <address>`
+ - only accepts a literal address
+- `banlist`
+- `tp <player> <target>` / `teleport`
+- `gamemode <survival|creative|0|1> [player]` / `gm`
+
+CLI behavior notes:
+- Command parsing accepts both `cmd` and `/cmd`.
+- Quoted arguments are supported by `ServerCliParser`.
+- Completion is implemented per command via `Complete(...)`.
+
+### 4.3 Files Written Next to the Executable
+- `server.properties`
+- `banned-players.json`
+- `banned-ips.json`
+
+This follows from `SetExeWorkingDirectory()`, so these files are resolved relative to `Minecraft.Server.exe`, not the shell directory you launched from.
+
+## 5. Common Development Tasks
+
+### 5.1 Add a New CLI Command
+
+Use this pattern when adding commands like `/kick`, `/time`, etc.
+
+1. Add files under `Console/commands/`
+ - `CliCommandYourCommand.h`
+ - `CliCommandYourCommand.cpp`
+2. Implement `IServerCliCommand`
+ - `Name()`, `Usage()`, `Description()`, `Execute(...)`
+ - optional: `Aliases()` and `Complete(...)`
+3. Register the command in `ServerCliEngine::RegisterDefaultCommands()`.
+4. Add source/header to build definitions:
+ - `CMakeLists.txt` (`MINECRAFT_SERVER_SOURCES`)
+ - `Minecraft.Server/Minecraft.Server.vcxproj` (`<ClCompile>` / `<ClInclude>`)
+5. Manual verify:
+ - command appears in `help`
+ - command executes correctly
+ - completion works for both `cmd` and `/cmd`
+ - quoted arguments behave as expected
+
+Implementation references:
+- `CliCommandHelp.cpp` for a simple no-arg command
+- `CliCommandTp.cpp` for multi-arg + completion + runtime checks
+- `CliCommandGamemode.cpp` for argument parsing and aliases
+- `CliCommandBanIp.cpp` for access-backed behavior with connection metadata
+
+### 5.2 Add or Change a `server.properties` Key
+
+1. Add/update the field in `ServerPropertiesConfig` (`ServerProperties.h`).
+2. Add a default entry to `kServerPropertyDefaults` (`ServerProperties.cpp`).
+3. Load and normalize the value in `LoadServerPropertiesConfig()`.
+ - Use existing helpers for bool/int/string/int64/log level/level type.
+4. If this value should be written back, update `SaveServerPropertiesConfig()`.
+ - Note: today that function intentionally only persists world identity.
+5. Apply it to runtime where needed:
+ - `ApplyServerPropertiesToDedicatedConfig(...)`
+ - host options in `ServerMain.cpp` (`app.SetGameHostOption(...)`)
+ - `PrintUsage()` / `ParseCommandLine()` if the key also gets a CLI override
+6. Manual verify:
+ - missing key regeneration
+ - invalid value normalization
+ - clamped ranges still make sense
+ - runtime behavior reflects the new value
+
+Normalization details worth remembering:
+- `level-id` is normalized to a safe save ID and length-limited.
+- `server-name` is capped to 16 runtime chars.
+- `max-players` is clamped to `1..8`.
+- `autosave-interval` is clamped to `5..3600`.
+- `level-type` normalizes to `default` or `flat`.
+
+### 5.3 Change Ban / Access Behavior
+
+Primary code lives in `Access/Access.cpp`, `Access/BanManager.cpp`, and `ServerLogManager.cpp`.
+
+When changing this area:
+- Keep `BanManager` responsible for storage/caching, not live-network policy.
+- Keep the clone-and-publish snapshot pattern in `Access.cpp` so readers never block on disk I/O.
+- Remember that `ban-ip <player>` depends on `ServerLogManager::TryGetConnectionRemoteIp(...)`.
+- Keep expired entries out of `SnapshotBannedPlayers()` / `SnapshotBannedIps()` output.
+- Verify:
+ - clean boot creates empty ban files when missing
+ - `ban`, `ban-ip`, `pardon`, `pardon-ip`, and `banlist` still work
+ - online bans disconnect live targets immediately
+ - manual edits still reload safely if you later add or extend reload paths
+
+### 5.4 Change World Load/Create Behavior
+
+Primary code is in `WorldManager.cpp`.
+
+Current matching policy:
+1. Match by `level-id` (`UTF8SaveFilename`) first.
+2. Fall back to world-name match on title/file name.
+
+When changing this logic:
+- Keep `ApplyWorldStorageTarget(...)` usage consistent (title + save ID together).
+- Preserve periodic ticking in wait loops (`tickProc`) to avoid async deadlocks.
+- Keep timeout/error logs specific enough for diagnosis.
+- Verify:
+ - existing world is reused correctly
+ - no accidental new save directory creation
+ - shutdown save still succeeds
+ - newly created worlds still get the explicit initial save from `ServerMain.cpp`
+
+### 5.5 Add Logging for New Feature Work
+
+Use `ServerLogger` helpers:
+- `LogDebug`, `LogInfo`, `LogWarn`, `LogError`
+- formatted variants `LogDebugf`, `LogInfof`, etc.
+
+Use `ServerLogManager` when the event is specifically part of the transport/login/disconnect lifecycle.
+
+Recommended categories:
+- `startup` for init/shutdown lifecycle
+- `world-io` for save/world operations
+- `console` for CLI command handling
+- `access` for ban/access control state
+- `network` for connection/login audit
+
+## 6. Build and Run
+
+From repository root:
+
+```powershell
+cmake -S . -B build -G "Visual Studio 17 2022" -A x64
+cmake --build build --config Debug --target MinecraftServer
+cd .\build\Debug
+.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -maxplayers 8 -name DedicatedServer
+```
+
+Notes:
+- The process switches its working directory to the executable directory at startup.
+- `server.properties`, `banned-players.json`, and `banned-ips.json` are therefore read/written next to the executable.
+- For Visual Studio workflow, see root `COMPILE.md`.
+
+## 7. Safety Checklist Before Commit
+
+- the server starts without crash when `server.properties` is missing or sparse
+- missing access files are recreated on a clean boot
+- existing world loads by expected `level-id`
+- new world creation still performs the explicit initial save
+- CLI input and completion remain responsive
+- `banlist` output stays sane after adding/removing bans
+- no busy-wait path removed from async wait loops
+- both CMake and `.vcxproj` include newly added source files
+
+## 8. Quick Troubleshooting
+
+- Unknown command:
+ - check `RegisterDefaultCommands()` and build-file entries
+- `server.properties` or ban files seem to load from the wrong folder:
+ - remember `SetExeWorkingDirectory()` moves the working directory to the executable folder
+- Autosave or shutdown save timing out:
+ - confirm wait loops still call `TickCoreSystems()` and `HandleXuiActions()` where required
+- World not reused on restart:
+ - inspect `level-id` normalization and matching logic in `WorldManager.cpp`
+- `ban-ip <player>` cannot resolve an address:
+ - confirm the player is currently online and `ServerLogManager` has a cached remote IP for that connection
+- Settings not applied:
+ - confirm the value is loaded into `ServerPropertiesConfig`, optionally copied into `DedicatedServerConfig`, and then applied in `ServerMain.cpp`
diff --git a/Minecraft.Server/docs/DEVELOPMENT.ja.md b/Minecraft.Server/docs/DEVELOPMENT.ja.md
new file mode 100644
index 00000000..70d05aaa
--- /dev/null
+++ b/Minecraft.Server/docs/DEVELOPMENT.ja.md
@@ -0,0 +1,286 @@
+# Minecraft.Server 開発ガイド (日本語)
+
+この文書は、`Minecraft.Server` に新しく入る開発者が、安全に機能追加や改修を行うための実践的な地図として使うことを想定しています
+
+## 1. このサーバーが担うこと
+
+`Minecraft.Server` は、このコードベースにおける専用サーバー実行ファイルのエントリーポイントです
+
+主な責務:
+- 相対パスのファイル I/O を行う前に、カレントディレクトリを実行ファイルのあるフォルダへ切り替える
+- `server.properties` を読み込み、正規化し、不足や不正値を補完する
+- 専用サーバー向けランタイム、接続ログ、アクセス制御を初期化する
+- 対象ワールドをロードまたは新規作成し、実際のセーブ先と `level-id` を整合させる
+- 専用サーバーのメインループを回す (network tick, XUI actions, autosave, CLI input)
+- `banned-players.json` や `banned-ips.json` など運用向けファイルを維持する
+- 新規ワールドの初回保存を実行し、その後安全にシャットダウンする
+
+## 2. 重要ファイル
+
+### 起動とランタイム
+- `Windows64/ServerMain.cpp`
+ - `PrintUsage()` と `ParseCommandLine()`
+ - `SetExeWorkingDirectory()`
+ - 起動/終了フロー
+ - 新規ワールド初回保存の経路
+ - メインループ、オートセーブ、CLI ポーリング
+
+### ワールド選択とセーブ読込
+- `WorldManager.h`
+- `WorldManager.cpp`
+ - `level-id` 優先、その後 world 名フォールバックでセーブ探索
+ - storage title と save ID を常にセットで適用
+ - 非同期 storage/server action 完了待ちの helper を提供
+
+### サーバー設定
+- `ServerProperties.h`
+- `ServerProperties.cpp`
+ - 既定値と正規化レンジ
+ - `server.properties` の読込/補修/書込
+ - `ServerPropertiesConfig` の提供
+ - `SaveServerPropertiesConfig()` は `level-name` / `level-id` / `white-list` を書き換える
+
+### アクセス制御と BAN / Whitelist 永続化
+- `Access/Access.h`
+- `Access/Access.cpp`
+ - プロセス全体で使うアクセス制御 facade
+ - コンソールコマンドとログイン判定から参照される公開スナップショット管理
+- `Access/BanManager.h`
+- `Access/BanManager.cpp`
+ - `banned-players.json` と `banned-ips.json` の読込/書込
+ - 識別子の正規化と、期限切れエントリを除いた snapshot 出力
+- `Access/WhitelistManager.h`
+- `Access/WhitelistManager.cpp`
+ - `whitelist.json` の読込/書込
+ - ログイン判定と CLI で使う XUID whitelist の正規化管理
+
+### ログと接続監査
+- `ServerLogger.h`
+- `ServerLogger.cpp`
+ - ログレベル解釈
+ - 色付き/タイムスタンプ付きコンソールログ
+ - `startup`, `world-io`, `console`, `access`, `network`, `shutdown` などのカテゴリ
+- `ServerLogManager.h`
+- `ServerLogManager.cpp`
+ - TCP 接続 accept/reject ログ
+ - ログイン/切断の監査ログ
+ - `ban-ip <player>` が使う remote IP キャッシュ
+
+### コンソールコマンドシステム
+- `Console/ServerCli.cpp` (facade)
+- `Console/ServerCliInput.cpp` (linenoise 入力スレッド + completion bridge)
+- `Console/ServerCliParser.cpp` (トークン分解、クォート、補完コンテキスト)
+- `Console/ServerCliEngine.cpp` (実行ディスパッチ、補完、共通ヘルパー)
+- `Console/ServerCliRegistry.cpp` (登録と名前解決)
+- `Console/commands/*` (各コマンド実装)
+
+## 3. 起動フロー全体
+
+`Windows64/ServerMain.cpp` の主な流れ:
+1. `SetExeWorkingDirectory()` でカレントディレクトリを実行ファイルのフォルダへ切り替える
+2. `LoadServerPropertiesConfig()` で `server.properties` を読み込み、正規化する
+3. `DedicatedServerConfig` へ反映したあと、CLI 引数で上書きする (`-port`, `-ip`/`-bind`, `-name`, `-maxplayers`, `-seed`, `-loglevel`, `-help`/`--help`/`-h`)
+4. プロセス状態、`ServerLogManager`、`Access::Initialize(".")` を初期化する
+5. window/device/profile/network/thread-local 系を初期化する
+6. `ServerPropertiesConfig` をゲームホスト設定へ反映する
+7. `BootstrapWorldForServer(...)` でワールドを決定する
+8. 読み込まれたセーブ ID が正規化後に変わった場合は、`SaveServerPropertiesConfig()` で書き戻す
+9. `RunNetworkGameThreadProc` でホストゲームスレッドを起動する
+10. 新規ワールドが作成された場合は、専用サーバー側で明示的に初回保存を要求する
+11. メインループに入る:
+ - `TickCoreSystems()`
+ - `HandleXuiActions()`
+ - `serverCli.Poll()`
+ - オートセーブスケジュール
+12. 終了時:
+ - CLI 入力を停止
+ - save-on-exit を要求してサーバー停止
+ - ネットワーク停止完了を待機
+ - ログ/アクセス制御/ネットワーク/デバイスを終了
+
+## 4. 現在の運用インターフェース
+
+### 4.1 起動引数
+- `-port <1-65535>`
+- `-ip <addr>` または `-bind <addr>`
+- `-name <name>` (実行時上限 16 文字)
+- `-maxplayers <1-8>`
+- `-seed <int64>`
+- `-loglevel <debug|info|warn|error>`
+- `-help`, `--help`, `-h`
+
+補足:
+- CLI による上書きは、その起動中のプロセスにだけ効きます
+- 現在サーバーが書き戻す値は `level-name` と `level-id` だけで、ワールド解決時に識別情報が変わった場合に限られます
+
+### 4.2 組み込みコンソールコマンド
+- `help` / `?`
+- `stop`
+- `list`
+- `ban <player> [reason ...]`
+ - 現状では対象プレイヤーがオンラインである必要があります
+- `ban-ip <address|player> [reason ...]`
+ - リテラル IPv4/IPv6 か、オンラインプレイヤーの現在 IP を対象にできます
+- `pardon <player>`
+- `pardon-ip <address>`
+ - リテラルアドレスのみ受け付けます
+- `banlist`
+- `tp <player> <target>` / `teleport`
+- `gamemode <survival|creative|0|1> [player]` / `gm`
+
+CLI 挙動の補足:
+- `cmd` と `/cmd` の両方を受け付けます
+- `ServerCliParser` により引用符付き引数を扱えます
+- 補完は各コマンドの `Complete(...)` で実装します
+
+### 4.3 実行ファイル横に書かれるファイル
+- `server.properties`
+- `banned-players.json`
+- `banned-ips.json`
+
+これは `SetExeWorkingDirectory()` による挙動ですつまり、これらのファイルはシェル上の起動場所ではなく `Minecraft.Server.exe` 基準で解決されます
+
+## 5. よくある開発作業
+
+### 5.1 CLI コマンドを追加する
+
+`/kick` や `/time` のようなコマンド追加時の基本手順:
+
+1. `Console/commands/` にファイルを追加
+ - `CliCommandYourCommand.h`
+ - `CliCommandYourCommand.cpp`
+2. `IServerCliCommand` を実装
+ - `Name()`, `Usage()`, `Description()`, `Execute(...)`
+ - 必要なら `Aliases()` と `Complete(...)`
+3. `ServerCliEngine::RegisterDefaultCommands()` に登録する
+4. ビルド定義に追加する
+ - `CMakeLists.txt` (`MINECRAFT_SERVER_SOURCES`)
+ - `Minecraft.Server/Minecraft.Server.vcxproj` (`<ClCompile>` / `<ClInclude>`)
+5. 手動確認
+ - `help` に表示される
+ - 実行結果が期待通り
+ - 補完が `cmd` と `/cmd` の両方で動く
+ - 引用符付き引数が期待通り処理される
+
+参考実装:
+- `CliCommandHelp.cpp` (単純コマンド)
+- `CliCommandTp.cpp` (複数引数 + 補完 + 実行時チェック)
+- `CliCommandGamemode.cpp` (引数解釈 + エイリアス)
+- `CliCommandBanIp.cpp` (接続メタデータを使うアクセス制御系コマンド)
+
+### 5.2 `server.properties` キーを追加/変更する
+
+1. `ServerProperties.h` の `ServerPropertiesConfig` にフィールドを追加/更新する
+2. `ServerProperties.cpp` の `kServerPropertyDefaults` に既定値を追加する
+3. `LoadServerPropertiesConfig()` で読み込みと正規化を実装する
+ - bool/int/string/int64/log level/level type 用の既存 helper を使う
+4. 書き戻し対象にしたいなら `SaveServerPropertiesConfig()` を更新する
+ - ただし現状この関数は、意図的にワールド識別情報だけを永続化します
+5. 実行時反映箇所を更新する:
+ - `ApplyServerPropertiesToDedicatedConfig(...)`
+ - `ServerMain.cpp` の `app.SetGameHostOption(...)`
+ - CLI 上書きも持たせるなら `PrintUsage()` / `ParseCommandLine()`
+6. 手動確認:
+ - 欠損キーの自動補完
+ - 不正値の正規化
+ - clamp 範囲が妥当か
+ - 実行時挙動に反映されるか
+
+> 覚えておくと良い正規化ポイント
+- `level-id` は安全な save ID に正規化され、長さ制限も掛かる
+- `server-name` は実行時 16 文字まで
+- `max-players` は `1..8` に clamp される(あとで増やす必要あり)
+- `autosave-interval` は `5..3600` に clamp される
+- `level-type` は `default` または `flat` に正規化される
+
+### 5.3 BAN / アクセス制御挙動を変更する
+
+主な実装は `Access/Access.cpp`, `Access/BanManager.cpp`, `ServerLogManager.cpp` にあります
+
+変更時の注意:
+- `BanManager` は storage/caching に責務を寄せ、 live-network policy を持ち込みすぎない
+- `Access.cpp` の clone-and-publish スナップショット方式を保ち、読取側がディスク I/O で止まらないようにする
+- `ban-ip <player>` は `ServerLogManager::TryGetConnectionRemoteIp(...)` に依存することを忘れない
+- `SnapshotBannedPlayers()` / `SnapshotBannedIps()` には期限切れエントリを混ぜない
+- 確認項目:
+ - 欠損時に空の BAN ファイルが初回起動で生成される
+ - `ban`, `ban-ip`, `pardon`, `pardon-ip`, `banlist` が動く
+ - オンライン対象の BAN が即時切断まで到達する
+ - 将来 reload 経路を増やしても手動編集が安全に再読込できる
+
+### 5.4 ワールドロード/新規作成ロジックを変更する
+
+主な実装は `WorldManager.cpp` にあります
+
+現在の探索ポリシー:
+1. `level-id` (`UTF8SaveFilename`) 完全一致を優先
+2. 失敗時に world 名一致へフォールバック
+
+変更時の注意:
+- `ApplyWorldStorageTarget(...)` で title と save ID を常にセットで扱う
+- 待機ループで `tickProc` を回し続ける
+ - これを止めると非同期進行が止まり、タイムアウトしやすくなる
+- タイムアウトや失敗ログは具体的に残す
+- 確認項目
+ - 既存ワールドを正しく再利用できるか
+ - 意図しない新規セーブ先が増えていないか
+ - 終了時保存が成功するか
+ - 新規ワールド時の明示的初回保存が `ServerMain.cpp` から維持されているか
+
+### 5.5 ログを追加する
+
+`ServerLogger` の API を利用:
+- `LogDebug`, `LogInfo`, `LogWarn`, `LogError`
+- フォーマット付きは `LogDebugf`, `LogInfof` など
+
+transport/login/disconnect ライフサイクルに属するイベントなら `ServerLogManager` 側を使います
+
+推奨カテゴリ:
+- `startup`: 起動ライフサイクル
+- `shutdown`: 停止ライフサイクル
+- `world-io`: ワールド/保存処理
+- `console`: CLI コマンド処理
+- `access`: BAN/アクセス制御状態
+- `network`: 接続/ログイン監査
+
+## 6. ビルドと実行
+
+リポジトリルートで実行:
+
+```powershell
+cmake -S . -B build -G "Visual Studio 17 2022" -A x64
+cmake --build build --config Debug --target MinecraftServer
+cd .\build\Debug
+.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -name DedicatedServer
+```
+
+補足:
+- プロセスは起動時にカレントディレクトリを実行ファイルの場所へ切り替えます
+- `server.properties`, `banned-players.json`, `banned-ips.json` はそのため実行ファイル横に読み書きされます
+- Visual Studio ワークフローはルートの `COMPILE.md` を参照してください
+
+## 7. 変更前チェックリスト
+
+- `server.properties` が欠損または疎でもクラッシュせず起動できる
+- 欠損したアクセス制御ファイルがクリーンブート時に再生成される
+- 既存ワールドが期待した `level-id` でロードされる
+- 新規ワールド作成時の明示的初回保存が維持される
+- CLI 入力と補完が引き続き応答する
+- `banlist` 出力が BAN 追加/解除後も破綻しない
+- 非同期待機ループから `TickCoreSystems()` など busy-wait 防止用ティックを消していない
+- 新規追加したソースが CMake と `.vcxproj` の両方に入っている
+
+## 8. クイックトラブルシュート
+
+- コマンドが認識されない:
+ - `RegisterDefaultCommands()` とビルド定義を確認する
+- `server.properties` や BAN ファイルの読込先が想定と違う:
+ - `SetExeWorkingDirectory()` により実行ファイルのフォルダへ移動していることを確認する
+- オートセーブ/終了時保存がタイムアウトする:
+ - 待機ループ内で `TickCoreSystems()` と `HandleXuiActions()` を回しているか確認する
+- 再起動時に同じワールドを使わない:
+ - `level-id` の正規化と `WorldManager.cpp` の一致判定を確認する
+- `ban-ip <player>` で IP を解決できない:
+ - 対象プレイヤーがオンラインで、`ServerLogManager` に接続 IP がキャッシュされているか確認する
+- 設定変更が効かない:
+ - 値が `ServerPropertiesConfig` にロードされ、必要なら `DedicatedServerConfig` にコピーされ、その後 `ServerMain.cpp` で反映されているか確認する
diff --git a/Minecraft.Server/vendor/linenoise/LICENSE b/Minecraft.Server/vendor/linenoise/LICENSE
new file mode 100644
index 00000000..d7391566
--- /dev/null
+++ b/Minecraft.Server/vendor/linenoise/LICENSE
@@ -0,0 +1,25 @@
+This vendored component is based on the linenoise project idea/API.
+
+Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Minecraft.Server/vendor/linenoise/linenoise.c b/Minecraft.Server/vendor/linenoise/linenoise.c
new file mode 100644
index 00000000..a5b8ac5f
--- /dev/null
+++ b/Minecraft.Server/vendor/linenoise/linenoise.c
@@ -0,0 +1,649 @@
+#include "linenoise.h"
+
+#include <conio.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+
+#define LINENOISE_MAX_LINE 4096
+#define LINENOISE_MAX_PROMPT 128
+
+typedef struct linenoiseHistory {
+ char **items;
+ int len;
+ int cap;
+ int maxLen;
+} linenoiseHistory;
+
+static linenoiseCompletionCallback *g_completionCallback = NULL;
+static volatile LONG g_stopRequested = 0;
+static linenoiseHistory g_history = { NULL, 0, 0, 128 };
+/* Guards redraw/log interleaving so prompt and log lines do not overlap. */
+static CRITICAL_SECTION g_ioLock;
+static volatile LONG g_ioLockState = 0; /* 0=not init, 1=init in progress, 2=ready */
+/* Snapshot of current editor line used to restore prompt after external output. */
+static volatile LONG g_editorActive = 0;
+static char g_editorPrompt[LINENOISE_MAX_PROMPT] = { 0 };
+static char g_editorBuf[LINENOISE_MAX_LINE] = { 0 };
+static int g_editorLen = 0;
+static int g_editorPos = 0;
+static int g_editorPrevLen = 0;
+
+/**
+ * Lazily initialize the console I/O critical section.
+ * This avoids static init order issues and keeps startup cost minimal.
+ */
+static void linenoiseEnsureIoLockInit(void)
+{
+ LONG state = InterlockedCompareExchange(&g_ioLockState, 0, 0);
+ if (state == 2)
+ return;
+
+ if (state == 0 && InterlockedCompareExchange(&g_ioLockState, 1, 0) == 0)
+ {
+ InitializeCriticalSection(&g_ioLock);
+ InterlockedExchange(&g_ioLockState, 2);
+ return;
+ }
+
+ while (InterlockedCompareExchange(&g_ioLockState, 0, 0) != 2)
+ {
+ Sleep(0);
+ }
+}
+
+static void linenoiseLockIo(void)
+{
+ linenoiseEnsureIoLockInit();
+ EnterCriticalSection(&g_ioLock);
+}
+
+static void linenoiseUnlockIo(void)
+{
+ LeaveCriticalSection(&g_ioLock);
+}
+
+/**
+ * Save current prompt/buffer/cursor state for later redraw.
+ * Called after each redraw while editor is active.
+ */
+static void linenoiseUpdateEditorState(const char *prompt, const char *buf, int len, int pos, int prevLen)
+{
+ if (prompt == NULL)
+ prompt = "";
+ if (buf == NULL)
+ buf = "";
+
+ strncpy_s(g_editorPrompt, sizeof(g_editorPrompt), prompt, _TRUNCATE);
+ strncpy_s(g_editorBuf, sizeof(g_editorBuf), buf, _TRUNCATE);
+ g_editorLen = len;
+ g_editorPos = pos;
+ g_editorPrevLen = prevLen;
+ InterlockedExchange(&g_editorActive, 1);
+}
+
+static void linenoiseDeactivateEditorState(void)
+{
+ InterlockedExchange(&g_editorActive, 0);
+ g_editorPrompt[0] = 0;
+ g_editorBuf[0] = 0;
+ g_editorLen = 0;
+ g_editorPos = 0;
+ g_editorPrevLen = 0;
+}
+
+static char *linenoiseStrdup(const char *src)
+{
+ size_t n = strlen(src) + 1;
+ char *out = (char *)malloc(n);
+ if (out == NULL)
+ return NULL;
+ memcpy(out, src, n);
+ return out;
+}
+
+static void linenoiseEnsureHistoryCapacity(int wanted)
+{
+ if (wanted <= g_history.cap)
+ return;
+
+ int newCap = g_history.cap == 0 ? 32 : g_history.cap;
+ while (newCap < wanted)
+ newCap *= 2;
+
+ char **newItems = (char **)realloc(g_history.items, sizeof(char *) * (size_t)newCap);
+ if (newItems == NULL)
+ return;
+
+ g_history.items = newItems;
+ g_history.cap = newCap;
+}
+
+static void linenoiseClearCompletions(linenoiseCompletions *lc)
+{
+ size_t i = 0;
+ for (i = 0; i < lc->len; ++i)
+ {
+ free(lc->cvec[i]);
+ }
+ free(lc->cvec);
+ lc->cvec = NULL;
+ lc->len = 0;
+}
+
+void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str)
+{
+ char **newVec = (char **)realloc(lc->cvec, sizeof(char *) * (lc->len + 1));
+ if (newVec == NULL)
+ return;
+
+ lc->cvec = newVec;
+ lc->cvec[lc->len] = linenoiseStrdup(str);
+ if (lc->cvec[lc->len] == NULL)
+ return;
+
+ lc->len += 1;
+}
+
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn)
+{
+ g_completionCallback = fn;
+}
+
+void linenoiseFree(void *ptr)
+{
+ free(ptr);
+}
+
+int linenoiseHistorySetMaxLen(int len)
+{
+ if (len <= 0)
+ return 0;
+
+ g_history.maxLen = len;
+ while (g_history.len > g_history.maxLen)
+ {
+ free(g_history.items[0]);
+ memmove(g_history.items, g_history.items + 1, sizeof(char *) * (size_t)(g_history.len - 1));
+ g_history.len -= 1;
+ }
+
+ return 1;
+}
+
+int linenoiseHistoryAdd(const char *line)
+{
+ if (line == NULL || line[0] == 0)
+ return 0;
+
+ if (g_history.len > 0)
+ {
+ const char *last = g_history.items[g_history.len - 1];
+ if (last != NULL && strcmp(last, line) == 0)
+ return 1;
+ }
+
+ linenoiseEnsureHistoryCapacity(g_history.len + 1);
+ if (g_history.cap <= g_history.len)
+ return 0;
+
+ g_history.items[g_history.len] = linenoiseStrdup(line);
+ if (g_history.items[g_history.len] == NULL)
+ return 0;
+
+ g_history.len += 1;
+
+ while (g_history.len > g_history.maxLen)
+ {
+ free(g_history.items[0]);
+ memmove(g_history.items, g_history.items + 1, sizeof(char *) * (size_t)(g_history.len - 1));
+ g_history.len -= 1;
+ }
+
+ return 1;
+}
+
+void linenoiseRequestStop(void)
+{
+ InterlockedExchange(&g_stopRequested, 1);
+}
+
+void linenoiseResetStop(void)
+{
+ InterlockedExchange(&g_stopRequested, 0);
+}
+
+static int linenoiseIsStopRequested(void)
+{
+ return InterlockedCompareExchange(&g_stopRequested, 0, 0) != 0;
+}
+
+static void linenoiseWriteHint(const char *hint, size_t hintLen)
+{
+ HANDLE stdoutHandle;
+ CONSOLE_SCREEN_BUFFER_INFO originalInfo;
+ int hasColorConsole = 0;
+
+ if (hint == NULL || hintLen == 0)
+ {
+ return;
+ }
+
+ stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (stdoutHandle != INVALID_HANDLE_VALUE && stdoutHandle != NULL)
+ {
+ if (GetConsoleScreenBufferInfo(stdoutHandle, &originalInfo))
+ {
+ hasColorConsole = 1;
+ /* Draw predictive tail in dim gray, then restore original console colors. */
+ SetConsoleTextAttribute(stdoutHandle, FOREGROUND_INTENSITY);
+ }
+ }
+
+ fwrite(hint, 1, hintLen, stdout);
+
+ if (hasColorConsole)
+ {
+ SetConsoleTextAttribute(stdoutHandle, originalInfo.wAttributes);
+ }
+}
+
+static int linenoiseStartsWithIgnoreCase(const char *full, const char *prefix)
+{
+ while (*prefix != 0)
+ {
+ if (tolower((unsigned char)*full) != tolower((unsigned char)*prefix))
+ {
+ return 0;
+ }
+ ++full;
+ ++prefix;
+ }
+ return 1;
+}
+
+static size_t linenoiseBuildHint(const char *buf, char *hint, size_t hintSize)
+{
+ linenoiseCompletions lc;
+ size_t inputLen = 0;
+ size_t i = 0;
+
+ if (hint == NULL || hintSize == 0)
+ {
+ return 0;
+ }
+ hint[0] = 0;
+
+ if (buf == NULL || buf[0] == 0 || g_completionCallback == NULL)
+ {
+ return 0;
+ }
+
+ lc.len = 0;
+ lc.cvec = NULL;
+ /* Reuse the completion callback and derive a "ghost text" suffix from the first extending match. */
+ g_completionCallback(buf, &lc);
+
+ inputLen = strlen(buf);
+ for (i = 0; i < lc.len; ++i)
+ {
+ const char *candidate = lc.cvec[i];
+ if (candidate == NULL)
+ {
+ continue;
+ }
+ if (strlen(candidate) <= inputLen)
+ {
+ continue;
+ }
+ if (!linenoiseStartsWithIgnoreCase(candidate, buf))
+ {
+ continue;
+ }
+
+ /* Keep only the part not yet typed by the user (rendered as hint text). */
+ strncpy_s(hint, hintSize, candidate + inputLen, _TRUNCATE);
+ break;
+ }
+
+ linenoiseClearCompletions(&lc);
+ return strlen(hint);
+}
+
+static void linenoiseRedrawUnsafe(const char *prompt, const char *buf, int len, int pos, int *prevLen)
+{
+ int i;
+ char hint[LINENOISE_MAX_LINE] = {0};
+ int renderedLen = len;
+ /* Hint length contributes to the rendered width so stale tail characters can be cleared correctly. */
+ int hintLen = (int)linenoiseBuildHint(buf, hint, sizeof(hint));
+ if (hintLen > 0)
+ {
+ renderedLen += hintLen;
+ }
+
+ fputc('\r', stdout);
+ fputs(prompt, stdout);
+ if (len > 0)
+ {
+ fwrite(buf, 1, (size_t)len, stdout);
+ }
+ if (hintLen > 0)
+ {
+ linenoiseWriteHint(hint, (size_t)hintLen);
+ }
+
+ if (*prevLen > renderedLen)
+ {
+ for (i = renderedLen; i < *prevLen; ++i)
+ {
+ fputc(' ', stdout);
+ }
+ }
+
+ fputc('\r', stdout);
+ fputs(prompt, stdout);
+ if (pos > 0)
+ {
+ /* Cursor positioning reflects only real user input, not the ghost hint suffix. */
+ fwrite(buf, 1, (size_t)pos, stdout);
+ }
+
+ fflush(stdout);
+ *prevLen = renderedLen;
+ linenoiseUpdateEditorState(prompt, buf, len, pos, *prevLen);
+}
+
+static void linenoiseRedraw(const char *prompt, const char *buf, int len, int pos, int *prevLen)
+{
+ linenoiseLockIo();
+ linenoiseRedrawUnsafe(prompt, buf, len, pos, prevLen);
+ linenoiseUnlockIo();
+}
+
+static int linenoiseStartsWith(const char *full, const char *prefix)
+{
+ while (*prefix != 0)
+ {
+ if (*full != *prefix)
+ return 0;
+ ++full;
+ ++prefix;
+ }
+ return 1;
+}
+
+static int linenoiseComputeCommonPrefix(const linenoiseCompletions *lc, const char *seed, char *out, size_t outSize)
+{
+ size_t commonLen = 0;
+ size_t i;
+
+ if (lc->len == 0 || outSize == 0)
+ return 0;
+
+ strncpy_s(out, outSize, lc->cvec[0], _TRUNCATE);
+ commonLen = strlen(out);
+
+ for (i = 1; i < lc->len; ++i)
+ {
+ const char *candidate = lc->cvec[i];
+ size_t j = 0;
+
+ while (j < commonLen && out[j] != 0 && candidate[j] != 0 && out[j] == candidate[j])
+ ++j;
+
+ commonLen = j;
+ out[commonLen] = 0;
+
+ if (commonLen == 0)
+ break;
+ }
+
+ if (strlen(out) <= strlen(seed))
+ return 0;
+
+ return linenoiseStartsWith(out, seed);
+}
+
+static void linenoiseApplyCompletion(const char *prompt, char *buf, int *len, int *pos, int *prevLen)
+{
+ linenoiseCompletions lc;
+ int i;
+
+ if (g_completionCallback == NULL)
+ {
+ Beep(750, 15);
+ return;
+ }
+
+ lc.len = 0;
+ lc.cvec = NULL;
+ g_completionCallback(buf, &lc);
+
+ if (lc.len == 0)
+ {
+ Beep(750, 15);
+ linenoiseClearCompletions(&lc);
+ return;
+ }
+
+ if (lc.len == 1)
+ {
+ strncpy_s(buf, LINENOISE_MAX_LINE, lc.cvec[0], _TRUNCATE);
+ *len = (int)strlen(buf);
+ *pos = *len;
+ linenoiseRedraw(prompt, buf, *len, *pos, prevLen);
+ linenoiseClearCompletions(&lc);
+ return;
+ }
+
+ {
+ char common[LINENOISE_MAX_LINE] = { 0 };
+ if (linenoiseComputeCommonPrefix(&lc, buf, common, sizeof(common)))
+ {
+ strncpy_s(buf, LINENOISE_MAX_LINE, common, _TRUNCATE);
+ *len = (int)strlen(buf);
+ *pos = *len;
+ linenoiseRedraw(prompt, buf, *len, *pos, prevLen);
+ }
+ }
+
+ linenoiseLockIo();
+ fputc('\n', stdout);
+ for (i = 0; i < (int)lc.len; ++i)
+ {
+ fputs(lc.cvec[i], stdout);
+ fputs(" ", stdout);
+ }
+ fputc('\n', stdout);
+ linenoiseRedrawUnsafe(prompt, buf, *len, *pos, prevLen);
+ linenoiseUnlockIo();
+ linenoiseClearCompletions(&lc);
+}
+
+char *linenoise(const char *prompt)
+{
+ char buf[LINENOISE_MAX_LINE];
+ int len = 0;
+ int pos = 0;
+ int prevLen = 0;
+ int historyIndex = g_history.len;
+
+ if (prompt == NULL)
+ prompt = "";
+
+ buf[0] = 0;
+ linenoiseLockIo();
+ linenoiseUpdateEditorState(prompt, buf, len, pos, prevLen);
+ fputs(prompt, stdout);
+ fflush(stdout);
+ linenoiseUnlockIo();
+
+ while (!linenoiseIsStopRequested())
+ {
+ if (!_kbhit())
+ {
+ Sleep(10);
+ continue;
+ }
+
+ {
+ int c = _getwch();
+
+ if (c == 0 || c == 224)
+ {
+ int ext = _getwch();
+ if (ext == 72)
+ {
+ if (g_history.len > 0 && historyIndex > 0)
+ {
+ historyIndex -= 1;
+ strncpy_s(buf, sizeof(buf), g_history.items[historyIndex], _TRUNCATE);
+ len = (int)strlen(buf);
+ pos = len;
+ linenoiseRedraw(prompt, buf, len, pos, &prevLen);
+ }
+ }
+ else if (ext == 80)
+ {
+ if (g_history.len > 0 && historyIndex < g_history.len)
+ {
+ historyIndex += 1;
+ if (historyIndex == g_history.len)
+ buf[0] = 0;
+ else
+ strncpy_s(buf, sizeof(buf), g_history.items[historyIndex], _TRUNCATE);
+
+ len = (int)strlen(buf);
+ pos = len;
+ linenoiseRedraw(prompt, buf, len, pos, &prevLen);
+ }
+ }
+ else if (ext == 75)
+ {
+ if (pos > 0)
+ {
+ pos -= 1;
+ linenoiseRedraw(prompt, buf, len, pos, &prevLen);
+ }
+ }
+ else if (ext == 77)
+ {
+ if (pos < len)
+ {
+ pos += 1;
+ linenoiseRedraw(prompt, buf, len, pos, &prevLen);
+ }
+ }
+ continue;
+ }
+
+ if (c == 3)
+ {
+ linenoiseLockIo();
+ linenoiseDeactivateEditorState();
+ fputc('\n', stdout);
+ fflush(stdout);
+ linenoiseUnlockIo();
+ return NULL;
+ }
+
+ if (c == '\r' || c == '\n')
+ {
+ char *out;
+ linenoiseLockIo();
+ linenoiseDeactivateEditorState();
+ fputc('\n', stdout);
+ fflush(stdout);
+ linenoiseUnlockIo();
+
+ out = linenoiseStrdup(buf);
+ return out;
+ }
+
+ if (c == '\t')
+ {
+ linenoiseApplyCompletion(prompt, buf, &len, &pos, &prevLen);
+ continue;
+ }
+
+ if (c == 8)
+ {
+ if (pos > 0 && len > 0)
+ {
+ memmove(buf + pos - 1, buf + pos, (size_t)(len - pos + 1));
+ pos -= 1;
+ len -= 1;
+ linenoiseRedraw(prompt, buf, len, pos, &prevLen);
+ }
+ continue;
+ }
+
+ if (isprint((unsigned char)c) && len < LINENOISE_MAX_LINE - 1)
+ {
+ if (pos == len)
+ {
+ buf[pos++] = (char)c;
+ len += 1;
+ buf[len] = 0;
+ }
+ else
+ {
+ memmove(buf + pos + 1, buf + pos, (size_t)(len - pos + 1));
+ buf[pos] = (char)c;
+ pos += 1;
+ len += 1;
+ }
+ linenoiseRedraw(prompt, buf, len, pos, &prevLen);
+ }
+ }
+ }
+
+ linenoiseLockIo();
+ linenoiseDeactivateEditorState();
+ fputc('\n', stdout);
+ fflush(stdout);
+ linenoiseUnlockIo();
+ return NULL;
+}
+
+void linenoiseExternalWriteBegin(void)
+{
+ int i;
+ int totalChars = 0;
+
+ /* Lock shared console state and clear current prompt area before external output. */
+ linenoiseLockIo();
+ if (InterlockedCompareExchange(&g_editorActive, 0, 0) == 0)
+ {
+ return;
+ }
+
+ totalChars = (int)strlen(g_editorPrompt) + g_editorPrevLen;
+ if (totalChars < 0)
+ {
+ totalChars = 0;
+ }
+
+ fputc('\r', stdout);
+ for (i = 0; i < totalChars; ++i)
+ {
+ fputc(' ', stdout);
+ }
+ fputc('\r', stdout);
+ fflush(stdout);
+}
+
+void linenoiseExternalWriteEnd(void)
+{
+ /* Restore prompt line after external output has been printed. */
+ if (InterlockedCompareExchange(&g_editorActive, 0, 0) != 0)
+ {
+ int prevLen = g_editorPrevLen;
+ linenoiseRedrawUnsafe(g_editorPrompt, g_editorBuf, g_editorLen, g_editorPos, &prevLen);
+ g_editorPrevLen = prevLen;
+ }
+ linenoiseUnlockIo();
+}
diff --git a/Minecraft.Server/vendor/linenoise/linenoise.h b/Minecraft.Server/vendor/linenoise/linenoise.h
new file mode 100644
index 00000000..6f7a0d2b
--- /dev/null
+++ b/Minecraft.Server/vendor/linenoise/linenoise.h
@@ -0,0 +1,37 @@
+#ifndef VENDORED_LINENOISE_H
+#define VENDORED_LINENOISE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct linenoiseCompletions {
+ size_t len;
+ char **cvec;
+} linenoiseCompletions;
+
+typedef void(linenoiseCompletionCallback)(const char *buf, linenoiseCompletions *lc);
+
+char *linenoise(const char *prompt);
+void linenoiseFree(void *ptr);
+
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn);
+void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str);
+
+int linenoiseHistoryAdd(const char *line);
+int linenoiseHistorySetMaxLen(int len);
+
+void linenoiseRequestStop(void);
+void linenoiseResetStop(void);
+
+/* Wrap external stdout/stderr writes so active prompt can be cleared/restored safely. */
+void linenoiseExternalWriteBegin(void);
+void linenoiseExternalWriteEnd(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Minecraft.Server/vendor/nlohmann/LICENSE.MIT b/Minecraft.Server/vendor/nlohmann/LICENSE.MIT
new file mode 100644
index 00000000..a1dacc8d
--- /dev/null
+++ b/Minecraft.Server/vendor/nlohmann/LICENSE.MIT
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2013-2025 Niels Lohmann
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Minecraft.Server/vendor/nlohmann/json.hpp b/Minecraft.Server/vendor/nlohmann/json.hpp
new file mode 100644
index 00000000..82d69f7c
--- /dev/null
+++ b/Minecraft.Server/vendor/nlohmann/json.hpp
@@ -0,0 +1,25526 @@
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+/****************************************************************************\
+ * Note on documentation: The source files contain links to the online *
+ * documentation of the public API at https://json.nlohmann.me. This URL *
+ * contains the most recent documentation and should also be applicable to *
+ * previous versions; documentation for deprecated functions is not *
+ * removed, but marked deprecated. See "Generate documentation" section in *
+ * file docs/README.md. *
+\****************************************************************************/
+
+#ifndef INCLUDE_NLOHMANN_JSON_HPP_
+#define INCLUDE_NLOHMANN_JSON_HPP_
+
+#include <algorithm> // all_of, find, for_each
+#include <cstddef> // nullptr_t, ptrdiff_t, size_t
+#include <functional> // hash, less
+#include <initializer_list> // initializer_list
+#ifndef JSON_NO_IO
+ #include <iosfwd> // istream, ostream
+#endif // JSON_NO_IO
+#include <iterator> // random_access_iterator_tag
+#include <memory> // unique_ptr
+#include <string> // string, stoi, to_string
+#include <utility> // declval, forward, move, pair, swap
+#include <vector> // vector
+
+// #include <nlohmann/adl_serializer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <utility>
+
+// #include <nlohmann/detail/abi_macros.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// This file contains all macro definitions affecting or depending on the ABI
+
+#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
+ #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
+ #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0
+ #warning "Already included a different version of the library!"
+ #endif
+ #endif
+#endif
+
+#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
+#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
+
+#ifndef JSON_DIAGNOSTICS
+ #define JSON_DIAGNOSTICS 0
+#endif
+
+#ifndef JSON_DIAGNOSTIC_POSITIONS
+ #define JSON_DIAGNOSTIC_POSITIONS 0
+#endif
+
+#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
+#endif
+
+#if JSON_DIAGNOSTICS
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
+#else
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
+#endif
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp
+#else
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS
+#endif
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
+#else
+ #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
+ #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
+#endif
+
+// Construct the namespace ABI tags component
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
+#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
+ NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
+
+#define NLOHMANN_JSON_ABI_TAGS \
+ NLOHMANN_JSON_ABI_TAGS_CONCAT( \
+ NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
+ NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
+ NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)
+
+// Construct the namespace version component
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
+ _v ## major ## _ ## minor ## _ ## patch
+#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
+ NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
+
+#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
+#define NLOHMANN_JSON_NAMESPACE_VERSION
+#else
+#define NLOHMANN_JSON_NAMESPACE_VERSION \
+ NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
+ NLOHMANN_JSON_VERSION_MINOR, \
+ NLOHMANN_JSON_VERSION_PATCH)
+#endif
+
+// Combine namespace components
+#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
+#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
+ NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
+
+#ifndef NLOHMANN_JSON_NAMESPACE
+#define NLOHMANN_JSON_NAMESPACE \
+ nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
+ NLOHMANN_JSON_ABI_TAGS, \
+ NLOHMANN_JSON_NAMESPACE_VERSION)
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
+#define NLOHMANN_JSON_NAMESPACE_BEGIN \
+ namespace nlohmann \
+ { \
+ inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
+ NLOHMANN_JSON_ABI_TAGS, \
+ NLOHMANN_JSON_NAMESPACE_VERSION) \
+ {
+#endif
+
+#ifndef NLOHMANN_JSON_NAMESPACE_END
+#define NLOHMANN_JSON_NAMESPACE_END \
+ } /* namespace (inline namespace) NOLINT(readability/namespace) */ \
+ } // namespace nlohmann
+#endif
+
+// #include <nlohmann/detail/conversions/from_json.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // transform
+#include <array> // array
+#include <forward_list> // forward_list
+#include <iterator> // inserter, front_inserter, end
+#include <map> // map
+#ifdef JSON_HAS_CPP_17
+ #include <optional> // optional
+#endif
+#include <string> // string
+#include <tuple> // tuple, make_tuple
+#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
+#include <unordered_map> // unordered_map
+#include <utility> // pair, declval
+#include <valarray> // valarray
+
+// #include <nlohmann/detail/exceptions.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // nullptr_t
+#include <exception> // exception
+#if JSON_DIAGNOSTICS
+ #include <numeric> // accumulate
+#endif
+#include <stdexcept> // runtime_error
+#include <string> // to_string
+#include <vector> // vector
+
+// #include <nlohmann/detail/value_t.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t
+#include <string> // string
+
+// #include <nlohmann/detail/macro_scope.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <utility> // declval, pair
+// #include <nlohmann/detail/meta/detected.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <type_traits>
+
+// #include <nlohmann/detail/meta/void_t.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename ...Ts> struct make_void
+{
+ using type = void;
+};
+template<typename ...Ts> using void_t = typename make_void<Ts...>::type;
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// https://en.cppreference.com/w/cpp/experimental/is_detected
+struct nonesuch
+{
+ nonesuch() = delete;
+ ~nonesuch() = delete;
+ nonesuch(nonesuch const&) = delete;
+ nonesuch(nonesuch const&&) = delete;
+ void operator=(nonesuch const&) = delete;
+ void operator=(nonesuch&&) = delete;
+};
+
+template<class Default,
+ class AlwaysVoid,
+ template<class...> class Op,
+ class... Args>
+struct detector
+{
+ using value_t = std::false_type;
+ using type = Default;
+};
+
+template<class Default, template<class...> class Op, class... Args>
+struct detector<Default, void_t<Op<Args...>>, Op, Args...>
+{
+ using value_t = std::true_type;
+ using type = Op<Args...>;
+};
+
+template<template<class...> class Op, class... Args>
+using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
+
+template<template<class...> class Op, class... Args>
+struct is_detected_lazy : is_detected<Op, Args...> { };
+
+template<template<class...> class Op, class... Args>
+using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or = detector<Default, void, Op, Args...>;
+
+template<class Default, template<class...> class Op, class... Args>
+using detected_or_t = typename detected_or<Default, Op, Args...>::type;
+
+template<class Expected, template<class...> class Op, class... Args>
+using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
+
+template<class To, template<class...> class Op, class... Args>
+using is_detected_convertible =
+ std::is_convertible<detected_t<Op, Args...>, To>;
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/thirdparty/hedley/hedley.hpp>
+
+
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2016 - 2021 Evan Nemerson <evan@nemerson.com>
+// SPDX-License-Identifier: MIT
+
+/* Hedley - https://nemequ.github.io/hedley
+ * Created by Evan Nemerson <evan@nemerson.com>
+ */
+
+#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15)
+#if defined(JSON_HEDLEY_VERSION)
+ #undef JSON_HEDLEY_VERSION
+#endif
+#define JSON_HEDLEY_VERSION 15
+
+#if defined(JSON_HEDLEY_STRINGIFY_EX)
+ #undef JSON_HEDLEY_STRINGIFY_EX
+#endif
+#define JSON_HEDLEY_STRINGIFY_EX(x) #x
+
+#if defined(JSON_HEDLEY_STRINGIFY)
+ #undef JSON_HEDLEY_STRINGIFY
+#endif
+#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x)
+
+#if defined(JSON_HEDLEY_CONCAT_EX)
+ #undef JSON_HEDLEY_CONCAT_EX
+#endif
+#define JSON_HEDLEY_CONCAT_EX(a,b) a##b
+
+#if defined(JSON_HEDLEY_CONCAT)
+ #undef JSON_HEDLEY_CONCAT
+#endif
+#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b)
+
+#if defined(JSON_HEDLEY_CONCAT3_EX)
+ #undef JSON_HEDLEY_CONCAT3_EX
+#endif
+#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c
+
+#if defined(JSON_HEDLEY_CONCAT3)
+ #undef JSON_HEDLEY_CONCAT3
+#endif
+#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c)
+
+#if defined(JSON_HEDLEY_VERSION_ENCODE)
+ #undef JSON_HEDLEY_VERSION_ENCODE
+#endif
+#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR)
+ #undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR)
+ #undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)
+
+#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION)
+ #undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#endif
+#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)
+
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+ #undef JSON_HEDLEY_GNUC_VERSION
+#endif
+#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)
+ #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#elif defined(__GNUC__)
+ #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK)
+ #undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GNUC_VERSION)
+ #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION)
+ #undef JSON_HEDLEY_MSVC_VERSION
+#endif
+#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL)
+ #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)
+#elif defined(_MSC_FULL_VER) && !defined(__ICL)
+ #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)
+#elif defined(_MSC_VER) && !defined(__ICL)
+ #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK)
+ #undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#endif
+#if !defined(JSON_HEDLEY_MSVC_VERSION)
+ #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)
+#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
+ #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+ #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))
+#else
+ #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+ #undef JSON_HEDLEY_INTEL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL)
+ #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)
+#elif defined(__INTEL_COMPILER) && !defined(__ICL)
+ #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK)
+ #undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_VERSION)
+ #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+ #undef JSON_HEDLEY_INTEL_CL_VERSION
+#endif
+#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL)
+ #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0)
+#endif
+
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK)
+ #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_INTEL_CL_VERSION)
+ #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION)
+ #undef JSON_HEDLEY_PGI_VERSION
+#endif
+#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)
+ #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)
+#endif
+
+#if defined(JSON_HEDLEY_PGI_VERSION_CHECK)
+ #undef JSON_HEDLEY_PGI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PGI_VERSION)
+ #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+ #undef JSON_HEDLEY_SUNPRO_VERSION
+#endif
+#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
+ #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)
+#elif defined(__SUNPRO_C)
+ #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)
+#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
+ #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)
+#elif defined(__SUNPRO_CC)
+ #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK)
+ #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_SUNPRO_VERSION)
+ #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+ #undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#endif
+#if defined(__EMSCRIPTEN__)
+ #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)
+#endif
+
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK)
+ #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION)
+ #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION)
+ #undef JSON_HEDLEY_ARM_VERSION
+#endif
+#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)
+ #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)
+#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)
+ #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)
+#endif
+
+#if defined(JSON_HEDLEY_ARM_VERSION_CHECK)
+ #undef JSON_HEDLEY_ARM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_ARM_VERSION)
+ #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION)
+ #undef JSON_HEDLEY_IBM_VERSION
+#endif
+#if defined(__ibmxl__)
+ #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)
+#elif defined(__xlC__) && defined(__xlC_ver__)
+ #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)
+#elif defined(__xlC__)
+ #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)
+#endif
+
+#if defined(JSON_HEDLEY_IBM_VERSION_CHECK)
+ #undef JSON_HEDLEY_IBM_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IBM_VERSION)
+ #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION)
+ #undef JSON_HEDLEY_TI_VERSION
+#endif
+#if \
+ defined(__TI_COMPILER_VERSION__) && \
+ ( \
+ defined(__TMS470__) || defined(__TI_ARM__) || \
+ defined(__MSP430__) || \
+ defined(__TMS320C2000__) \
+ )
+#if (__TI_COMPILER_VERSION__ >= 16000000)
+ #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+#endif
+
+#if defined(JSON_HEDLEY_TI_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_VERSION)
+ #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+ #undef JSON_HEDLEY_TI_CL2000_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__)
+ #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL2000_VERSION)
+ #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+ #undef JSON_HEDLEY_TI_CL430_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__)
+ #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL430_VERSION)
+ #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+ #undef JSON_HEDLEY_TI_ARMCL_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__))
+ #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_ARMCL_VERSION)
+ #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+ #undef JSON_HEDLEY_TI_CL6X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__)
+ #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL6X_VERSION)
+ #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+ #undef JSON_HEDLEY_TI_CL7X_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__)
+ #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CL7X_VERSION)
+ #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+ #undef JSON_HEDLEY_TI_CLPRU_VERSION
+#endif
+#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__)
+ #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
+#endif
+
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK)
+ #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TI_CLPRU_VERSION)
+ #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+ #undef JSON_HEDLEY_CRAY_VERSION
+#endif
+#if defined(_CRAYC)
+ #if defined(_RELEASE_PATCHLEVEL)
+ #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)
+ #else
+ #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)
+ #endif
+#endif
+
+#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK)
+ #undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_CRAY_VERSION)
+ #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION)
+ #undef JSON_HEDLEY_IAR_VERSION
+#endif
+#if defined(__IAR_SYSTEMS_ICC__)
+ #if __VER__ > 1000
+ #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))
+ #else
+ #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0)
+ #endif
+#endif
+
+#if defined(JSON_HEDLEY_IAR_VERSION_CHECK)
+ #undef JSON_HEDLEY_IAR_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_IAR_VERSION)
+ #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+ #undef JSON_HEDLEY_TINYC_VERSION
+#endif
+#if defined(__TINYC__)
+ #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK)
+ #undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_TINYC_VERSION)
+ #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION)
+ #undef JSON_HEDLEY_DMC_VERSION
+#endif
+#if defined(__DMC__)
+ #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)
+#endif
+
+#if defined(JSON_HEDLEY_DMC_VERSION_CHECK)
+ #undef JSON_HEDLEY_DMC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_DMC_VERSION)
+ #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+ #undef JSON_HEDLEY_COMPCERT_VERSION
+#endif
+#if defined(__COMPCERT_VERSION__)
+ #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)
+#endif
+
+#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK)
+ #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_COMPCERT_VERSION)
+ #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+ #undef JSON_HEDLEY_PELLES_VERSION
+#endif
+#if defined(__POCC__)
+ #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)
+#endif
+
+#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK)
+ #undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_PELLES_VERSION)
+ #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+ #undef JSON_HEDLEY_MCST_LCC_VERSION
+#endif
+#if defined(__LCC__) && defined(__LCC_MINOR__)
+ #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__)
+#endif
+
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK)
+ #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_MCST_LCC_VERSION)
+ #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION)
+ #undef JSON_HEDLEY_GCC_VERSION
+#endif
+#if \
+ defined(JSON_HEDLEY_GNUC_VERSION) && \
+ !defined(__clang__) && \
+ !defined(JSON_HEDLEY_INTEL_VERSION) && \
+ !defined(JSON_HEDLEY_PGI_VERSION) && \
+ !defined(JSON_HEDLEY_ARM_VERSION) && \
+ !defined(JSON_HEDLEY_CRAY_VERSION) && \
+ !defined(JSON_HEDLEY_TI_VERSION) && \
+ !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CL430_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \
+ !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \
+ !defined(__COMPCERT__) && \
+ !defined(JSON_HEDLEY_MCST_LCC_VERSION)
+ #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION
+#endif
+
+#if defined(JSON_HEDLEY_GCC_VERSION_CHECK)
+ #undef JSON_HEDLEY_GCC_VERSION_CHECK
+#endif
+#if defined(JSON_HEDLEY_GCC_VERSION)
+ #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch))
+#else
+ #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_ATTRIBUTE)
+ #undef JSON_HEDLEY_HAS_ATTRIBUTE
+#endif
+#if \
+ defined(__has_attribute) && \
+ ( \
+ (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \
+ )
+# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)
+#else
+# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE)
+ #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+ #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE)
+ #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#endif
+#if defined(__has_attribute)
+ #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+#else
+ #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE)
+ #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#endif
+#if \
+ defined(__has_cpp_attribute) && \
+ defined(__cplusplus) && \
+ (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0))
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)
+#else
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS)
+ #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#endif
+#if !defined(__cplusplus) || !defined(__has_cpp_attribute)
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#elif \
+ !defined(JSON_HEDLEY_PGI_VERSION) && \
+ !defined(JSON_HEDLEY_IAR_VERSION) && \
+ (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \
+ (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0))
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute)
+#else
+ #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)
+ #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+ #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE)
+ #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#endif
+#if defined(__has_cpp_attribute) && defined(__cplusplus)
+ #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
+#else
+ #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_BUILTIN)
+ #undef JSON_HEDLEY_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+ #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)
+#else
+ #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN)
+ #undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+ #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN)
+ #undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#endif
+#if defined(__has_builtin)
+ #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
+#else
+ #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_FEATURE)
+ #undef JSON_HEDLEY_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+ #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature)
+#else
+ #define JSON_HEDLEY_HAS_FEATURE(feature) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE)
+ #undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+ #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_FEATURE)
+ #undef JSON_HEDLEY_GCC_HAS_FEATURE
+#endif
+#if defined(__has_feature)
+ #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
+#else
+ #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_EXTENSION)
+ #undef JSON_HEDLEY_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+ #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)
+#else
+ #define JSON_HEDLEY_HAS_EXTENSION(extension) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION)
+ #undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+ #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION)
+ #undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#endif
+#if defined(__has_extension)
+ #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
+#else
+ #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE)
+ #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+ #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)
+#else
+ #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)
+ #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+ #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)
+ #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#endif
+#if defined(__has_declspec_attribute)
+ #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
+#else
+ #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_HAS_WARNING)
+ #undef JSON_HEDLEY_HAS_WARNING
+#endif
+#if defined(__has_warning)
+ #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning)
+#else
+ #define JSON_HEDLEY_HAS_WARNING(warning) (0)
+#endif
+
+#if defined(JSON_HEDLEY_GNUC_HAS_WARNING)
+ #undef JSON_HEDLEY_GNUC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+ #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+ #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_GCC_HAS_WARNING)
+ #undef JSON_HEDLEY_GCC_HAS_WARNING
+#endif
+#if defined(__has_warning)
+ #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
+#else
+ #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if \
+ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+ defined(__clang__) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \
+ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \
+ (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))
+ #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value)
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+ #define JSON_HEDLEY_PRAGMA(value) __pragma(value)
+#else
+ #define JSON_HEDLEY_PRAGMA(value)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH)
+ #undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#endif
+#if defined(JSON_HEDLEY_DIAGNOSTIC_POP)
+ #undef JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+#if defined(__clang__)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))
+ #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))
+#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop")
+#elif \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
+ #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_PUSH
+ #define JSON_HEDLEY_DIAGNOSTIC_POP
+#endif
+
+/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for
+ HEDLEY INTERNAL USE ONLY. API subject to change without notice. */
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#endif
+#if defined(__cplusplus)
+# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat")
+# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions")
+# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions")
+# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+ _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+ _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \
+ xpr \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# else
+# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+ _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \
+ xpr \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# endif
+# else
+# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \
+ xpr \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# endif
+# endif
+#endif
+#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x
+#endif
+
+#if defined(JSON_HEDLEY_CONST_CAST)
+ #undef JSON_HEDLEY_CONST_CAST
+#endif
+#if defined(__cplusplus)
+# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))
+#elif \
+ JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \
+ ((T) (expr)); \
+ JSON_HEDLEY_DIAGNOSTIC_POP \
+ }))
+#else
+# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_REINTERPRET_CAST)
+ #undef JSON_HEDLEY_REINTERPRET_CAST
+#endif
+#if defined(__cplusplus)
+ #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))
+#else
+ #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_CAST)
+ #undef JSON_HEDLEY_STATIC_CAST
+#endif
+#if defined(__cplusplus)
+ #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))
+#else
+ #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr))
+#endif
+
+#if defined(JSON_HEDLEY_CPP_CAST)
+ #undef JSON_HEDLEY_CPP_CAST
+#endif
+#if defined(__cplusplus)
+# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast")
+# define JSON_HEDLEY_CPP_CAST(T, expr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \
+ ((T) (expr)) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0)
+# define JSON_HEDLEY_CPP_CAST(T, expr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("diag_suppress=Pe137") \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# else
+# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr))
+# endif
+#else
+# define JSON_HEDLEY_CPP_CAST(T, expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
+#elif \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215")
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))
+#elif \
+ JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)")
+#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292))
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030))
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098")
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)")
+#elif \
+ JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097")
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"")
+#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#endif
+
+#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION)
+ #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunused-function")
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"")
+#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505))
+#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142")
+#else
+ #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#endif
+
+#if defined(JSON_HEDLEY_DEPRECATED)
+ #undef JSON_HEDLEY_DEPRECATED
+#endif
+#if defined(JSON_HEDLEY_DEPRECATED_FOR)
+ #undef JSON_HEDLEY_DEPRECATED_FOR
+#endif
+#if \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since))
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement))
+#elif \
+ (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since)))
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement)))
+#elif defined(__cplusplus) && (__cplusplus >= 201402L)
+ #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]])
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]])
+#elif \
+ JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+ #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated)
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated")
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated")
+#else
+ #define JSON_HEDLEY_DEPRECATED(since)
+ #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement)
+#endif
+
+#if defined(JSON_HEDLEY_UNAVAILABLE)
+ #undef JSON_HEDLEY_UNAVAILABLE
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since)))
+#else
+ #define JSON_HEDLEY_UNAVAILABLE(available_since)
+#endif
+
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT)
+ #undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#endif
+#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG)
+ #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__))
+#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L)
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard)
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]])
+#elif defined(_Check_return_) /* SAL */
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_
+#else
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT
+ #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg)
+#endif
+
+#if defined(JSON_HEDLEY_SENTINEL)
+ #undef JSON_HEDLEY_SENTINEL
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))
+#else
+ #define JSON_HEDLEY_SENTINEL(position)
+#endif
+
+#if defined(JSON_HEDLEY_NO_RETURN)
+ #undef JSON_HEDLEY_NO_RETURN
+#endif
+#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_NO_RETURN __noreturn
+#elif \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+ #define JSON_HEDLEY_NO_RETURN _Noreturn
+#elif defined(__cplusplus) && (__cplusplus >= 201103L)
+ #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]])
+#elif \
+ JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+ #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+ #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return")
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+ #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+ #define JSON_HEDLEY_NO_RETURN __attribute((noreturn))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+ #define JSON_HEDLEY_NO_RETURN __declspec(noreturn)
+#else
+ #define JSON_HEDLEY_NO_RETURN
+#endif
+
+#if defined(JSON_HEDLEY_NO_ESCAPE)
+ #undef JSON_HEDLEY_NO_ESCAPE
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape)
+ #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__))
+#else
+ #define JSON_HEDLEY_NO_ESCAPE
+#endif
+
+#if defined(JSON_HEDLEY_UNREACHABLE)
+ #undef JSON_HEDLEY_UNREACHABLE
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE_RETURN)
+ #undef JSON_HEDLEY_UNREACHABLE_RETURN
+#endif
+#if defined(JSON_HEDLEY_ASSUME)
+ #undef JSON_HEDLEY_ASSUME
+#endif
+#if \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_ASSUME(expr) __assume(expr)
+#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume)
+ #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr)
+#elif \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+ #if defined(__cplusplus)
+ #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr)
+ #else
+ #define JSON_HEDLEY_ASSUME(expr) _nassert(expr)
+ #endif
+#endif
+#if \
+ (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable()
+#elif defined(JSON_HEDLEY_ASSUME)
+ #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+#if !defined(JSON_HEDLEY_ASSUME)
+ #if defined(JSON_HEDLEY_UNREACHABLE)
+ #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1)))
+ #else
+ #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr)
+ #endif
+#endif
+#if defined(JSON_HEDLEY_UNREACHABLE)
+ #if \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0)
+ #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value))
+ #else
+ #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE()
+ #endif
+#else
+ #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value)
+#endif
+#if !defined(JSON_HEDLEY_UNREACHABLE)
+ #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0)
+#endif
+
+JSON_HEDLEY_DIAGNOSTIC_PUSH
+#if JSON_HEDLEY_HAS_WARNING("-Wpedantic")
+ #pragma clang diagnostic ignored "-Wpedantic"
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus)
+ #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#endif
+#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0)
+ #if defined(__clang__)
+ #pragma clang diagnostic ignored "-Wvariadic-macros"
+ #elif defined(JSON_HEDLEY_GCC_VERSION)
+ #pragma GCC diagnostic ignored "-Wvariadic-macros"
+ #endif
+#endif
+#if defined(JSON_HEDLEY_NON_NULL)
+ #undef JSON_HEDLEY_NON_NULL
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+ #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))
+#else
+ #define JSON_HEDLEY_NON_NULL(...)
+#endif
+JSON_HEDLEY_DIAGNOSTIC_POP
+
+#if defined(JSON_HEDLEY_PRINTF_FORMAT)
+ #undef JSON_HEDLEY_PRINTF_FORMAT
+#endif
+#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))
+#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))
+#elif \
+ JSON_HEDLEY_HAS_ATTRIBUTE(format) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0)
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))
+#else
+ #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)
+#endif
+
+#if defined(JSON_HEDLEY_CONSTEXPR)
+ #undef JSON_HEDLEY_CONSTEXPR
+#endif
+#if defined(__cplusplus)
+ #if __cplusplus >= 201103L
+ #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr)
+ #endif
+#endif
+#if !defined(JSON_HEDLEY_CONSTEXPR)
+ #define JSON_HEDLEY_CONSTEXPR
+#endif
+
+#if defined(JSON_HEDLEY_PREDICT)
+ #undef JSON_HEDLEY_PREDICT
+#endif
+#if defined(JSON_HEDLEY_LIKELY)
+ #undef JSON_HEDLEY_LIKELY
+#endif
+#if defined(JSON_HEDLEY_UNLIKELY)
+ #undef JSON_HEDLEY_UNLIKELY
+#endif
+#if defined(JSON_HEDLEY_UNPREDICTABLE)
+ #undef JSON_HEDLEY_UNPREDICTABLE
+#endif
+#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable)
+ #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr))
+#endif
+#if \
+ (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability))
+# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability))
+# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability))
+# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 )
+# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 )
+#elif \
+ (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+# define JSON_HEDLEY_PREDICT(expr, expected, probability) \
+ (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)))
+# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \
+ (__extension__ ({ \
+ double hedley_probability_ = (probability); \
+ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \
+ }))
+# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \
+ (__extension__ ({ \
+ double hedley_probability_ = (probability); \
+ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \
+ }))
+# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1)
+# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
+#else
+# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))
+# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))
+# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))
+# define JSON_HEDLEY_LIKELY(expr) (!!(expr))
+# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr))
+#endif
+#if !defined(JSON_HEDLEY_UNPREDICTABLE)
+ #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5)
+#endif
+
+#if defined(JSON_HEDLEY_MALLOC)
+ #undef JSON_HEDLEY_MALLOC
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_MALLOC __attribute__((__malloc__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+ #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory")
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_MALLOC __declspec(restrict)
+#else
+ #define JSON_HEDLEY_MALLOC
+#endif
+
+#if defined(JSON_HEDLEY_PURE)
+ #undef JSON_HEDLEY_PURE
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+# define JSON_HEDLEY_PURE __attribute__((__pure__))
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data")
+#elif defined(__cplusplus) && \
+ ( \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \
+ )
+# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;")
+#else
+# define JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_CONST)
+ #undef JSON_HEDLEY_CONST
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(const) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_CONST __attribute__((__const__))
+#elif \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0)
+ #define JSON_HEDLEY_CONST _Pragma("no_side_effect")
+#else
+ #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE
+#endif
+
+#if defined(JSON_HEDLEY_RESTRICT)
+ #undef JSON_HEDLEY_RESTRICT
+#endif
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)
+ #define JSON_HEDLEY_RESTRICT restrict
+#elif \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
+ defined(__clang__) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_RESTRICT __restrict
+#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)
+ #define JSON_HEDLEY_RESTRICT _Restrict
+#else
+ #define JSON_HEDLEY_RESTRICT
+#endif
+
+#if defined(JSON_HEDLEY_INLINE)
+ #undef JSON_HEDLEY_INLINE
+#endif
+#if \
+ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
+ (defined(__cplusplus) && (__cplusplus >= 199711L))
+ #define JSON_HEDLEY_INLINE inline
+#elif \
+ defined(JSON_HEDLEY_GCC_VERSION) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0)
+ #define JSON_HEDLEY_INLINE __inline__
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_INLINE __inline
+#else
+ #define JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_ALWAYS_INLINE)
+ #undef JSON_HEDLEY_ALWAYS_INLINE
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+# define JSON_HEDLEY_ALWAYS_INLINE __forceinline
+#elif defined(__cplusplus) && \
+ ( \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \
+ )
+# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced")
+#else
+# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_NEVER_INLINE)
+ #undef JSON_HEDLEY_NEVER_INLINE
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
+ JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \
+ (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \
+ (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \
+ (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \
+ JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \
+ JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \
+ JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0)
+ #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__))
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0)
+ #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline")
+#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus)
+ #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+ #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never")
+#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
+ #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0)
+ #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline)
+#else
+ #define JSON_HEDLEY_NEVER_INLINE
+#endif
+
+#if defined(JSON_HEDLEY_PRIVATE)
+ #undef JSON_HEDLEY_PRIVATE
+#endif
+#if defined(JSON_HEDLEY_PUBLIC)
+ #undef JSON_HEDLEY_PUBLIC
+#endif
+#if defined(JSON_HEDLEY_IMPORT)
+ #undef JSON_HEDLEY_IMPORT
+#endif
+#if defined(_WIN32) || defined(__CYGWIN__)
+# define JSON_HEDLEY_PRIVATE
+# define JSON_HEDLEY_PUBLIC __declspec(dllexport)
+# define JSON_HEDLEY_IMPORT __declspec(dllimport)
+#else
+# if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+ JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+ ( \
+ defined(__TI_EABI__) && \
+ ( \
+ (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \
+ ) \
+ ) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden")))
+# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default")))
+# else
+# define JSON_HEDLEY_PRIVATE
+# define JSON_HEDLEY_PUBLIC
+# endif
+# define JSON_HEDLEY_IMPORT extern
+#endif
+
+#if defined(JSON_HEDLEY_NO_THROW)
+ #undef JSON_HEDLEY_NO_THROW
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__))
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0)
+ #define JSON_HEDLEY_NO_THROW __declspec(nothrow)
+#else
+ #define JSON_HEDLEY_NO_THROW
+#endif
+
+#if defined(JSON_HEDLEY_FALL_THROUGH)
+ #undef JSON_HEDLEY_FALL_THROUGH
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough)
+ #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]])
+#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough)
+ #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]])
+#elif defined(__fallthrough) /* SAL */
+ #define JSON_HEDLEY_FALL_THROUGH __fallthrough
+#else
+ #define JSON_HEDLEY_FALL_THROUGH
+#endif
+
+#if defined(JSON_HEDLEY_RETURNS_NON_NULL)
+ #undef JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+#if \
+ JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))
+#elif defined(_Ret_notnull_) /* SAL */
+ #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_
+#else
+ #define JSON_HEDLEY_RETURNS_NON_NULL
+#endif
+
+#if defined(JSON_HEDLEY_ARRAY_PARAM)
+ #undef JSON_HEDLEY_ARRAY_PARAM
+#endif
+#if \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+ !defined(__STDC_NO_VLA__) && \
+ !defined(__cplusplus) && \
+ !defined(JSON_HEDLEY_PGI_VERSION) && \
+ !defined(JSON_HEDLEY_TINYC_VERSION)
+ #define JSON_HEDLEY_ARRAY_PARAM(name) (name)
+#else
+ #define JSON_HEDLEY_ARRAY_PARAM(name)
+#endif
+
+#if defined(JSON_HEDLEY_IS_CONSTANT)
+ #undef JSON_HEDLEY_IS_CONSTANT
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR)
+ #undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#endif
+/* JSON_HEDLEY_IS_CONSTEXPR_ is for
+ HEDLEY INTERNAL USE ONLY. API subject to change without notice. */
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+ #undef JSON_HEDLEY_IS_CONSTEXPR_
+#endif
+#if \
+ JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+ JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \
+ (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10)
+ #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)
+#endif
+#if !defined(__cplusplus)
+# if \
+ JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
+ JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
+ JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24)
+#if defined(__INTPTR_TYPE__)
+ #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)
+#else
+ #include <stdint.h>
+ #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)
+#endif
+# elif \
+ ( \
+ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \
+ !defined(JSON_HEDLEY_SUNPRO_VERSION) && \
+ !defined(JSON_HEDLEY_PGI_VERSION) && \
+ !defined(JSON_HEDLEY_IAR_VERSION)) || \
+ (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \
+ JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
+ JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0)
+#if defined(__INTPTR_TYPE__)
+ #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)
+#else
+ #include <stdint.h>
+ #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)
+#endif
+# elif \
+ defined(JSON_HEDLEY_GCC_VERSION) || \
+ defined(JSON_HEDLEY_INTEL_VERSION) || \
+ defined(JSON_HEDLEY_TINYC_VERSION) || \
+ defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \
+ JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \
+ defined(JSON_HEDLEY_TI_CL2000_VERSION) || \
+ defined(JSON_HEDLEY_TI_CL6X_VERSION) || \
+ defined(JSON_HEDLEY_TI_CL7X_VERSION) || \
+ defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \
+ defined(__clang__)
+# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \
+ sizeof(void) != \
+ sizeof(*( \
+ 1 ? \
+ ((void*) ((expr) * 0L) ) : \
+((struct { char v[sizeof(void) * 2]; } *) 1) \
+ ) \
+ ) \
+ )
+# endif
+#endif
+#if defined(JSON_HEDLEY_IS_CONSTEXPR_)
+ #if !defined(JSON_HEDLEY_IS_CONSTANT)
+ #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr)
+ #endif
+ #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1))
+#else
+ #if !defined(JSON_HEDLEY_IS_CONSTANT)
+ #define JSON_HEDLEY_IS_CONSTANT(expr) (0)
+ #endif
+ #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)
+#endif
+
+#if defined(JSON_HEDLEY_BEGIN_C_DECLS)
+ #undef JSON_HEDLEY_BEGIN_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_END_C_DECLS)
+ #undef JSON_HEDLEY_END_C_DECLS
+#endif
+#if defined(JSON_HEDLEY_C_DECL)
+ #undef JSON_HEDLEY_C_DECL
+#endif
+#if defined(__cplusplus)
+ #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" {
+ #define JSON_HEDLEY_END_C_DECLS }
+ #define JSON_HEDLEY_C_DECL extern "C"
+#else
+ #define JSON_HEDLEY_BEGIN_C_DECLS
+ #define JSON_HEDLEY_END_C_DECLS
+ #define JSON_HEDLEY_C_DECL
+#endif
+
+#if defined(JSON_HEDLEY_STATIC_ASSERT)
+ #undef JSON_HEDLEY_STATIC_ASSERT
+#endif
+#if \
+ !defined(__cplusplus) && ( \
+ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \
+ (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \
+ JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
+ defined(_Static_assert) \
+ )
+# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)
+#elif \
+ (defined(__cplusplus) && (__cplusplus >= 201103L)) || \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message))
+#else
+# define JSON_HEDLEY_STATIC_ASSERT(expr, message)
+#endif
+
+#if defined(JSON_HEDLEY_NULL)
+ #undef JSON_HEDLEY_NULL
+#endif
+#if defined(__cplusplus)
+ #if __cplusplus >= 201103L
+ #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr)
+ #elif defined(NULL)
+ #define JSON_HEDLEY_NULL NULL
+ #else
+ #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0)
+ #endif
+#elif defined(NULL)
+ #define JSON_HEDLEY_NULL NULL
+#else
+ #define JSON_HEDLEY_NULL ((void*) 0)
+#endif
+
+#if defined(JSON_HEDLEY_MESSAGE)
+ #undef JSON_HEDLEY_MESSAGE
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+# define JSON_HEDLEY_MESSAGE(msg) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+ JSON_HEDLEY_PRAGMA(message msg) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg)
+#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0)
+# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg)
+#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0)
+# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0)
+# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+# define JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_WARNING)
+ #undef JSON_HEDLEY_WARNING
+#endif
+#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas")
+# define JSON_HEDLEY_WARNING(msg) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
+ JSON_HEDLEY_PRAGMA(clang warning msg) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+#elif \
+ JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \
+ JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
+ JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0)
+# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg)
+#elif \
+ JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg))
+#else
+# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg)
+#endif
+
+#if defined(JSON_HEDLEY_REQUIRE)
+ #undef JSON_HEDLEY_REQUIRE
+#endif
+#if defined(JSON_HEDLEY_REQUIRE_MSG)
+ #undef JSON_HEDLEY_REQUIRE_MSG
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if)
+# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat")
+# define JSON_HEDLEY_REQUIRE(expr) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+ __attribute__((diagnose_if(!(expr), #expr, "error"))) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
+ __attribute__((diagnose_if(!(expr), msg, "error"))) \
+ JSON_HEDLEY_DIAGNOSTIC_POP
+# else
+# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error")))
+# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error")))
+# endif
+#else
+# define JSON_HEDLEY_REQUIRE(expr)
+# define JSON_HEDLEY_REQUIRE_MSG(expr,msg)
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS)
+ #undef JSON_HEDLEY_FLAGS
+#endif
+#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion"))
+ #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__))
+#else
+ #define JSON_HEDLEY_FLAGS
+#endif
+
+#if defined(JSON_HEDLEY_FLAGS_CAST)
+ #undef JSON_HEDLEY_FLAGS_CAST
+#endif
+#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0)
+# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \
+ JSON_HEDLEY_DIAGNOSTIC_PUSH \
+ _Pragma("warning(disable:188)") \
+ ((T) (expr)); \
+ JSON_HEDLEY_DIAGNOSTIC_POP \
+ }))
+#else
+# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr)
+#endif
+
+#if defined(JSON_HEDLEY_EMPTY_BASES)
+ #undef JSON_HEDLEY_EMPTY_BASES
+#endif
+#if \
+ (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \
+ JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0)
+ #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases)
+#else
+ #define JSON_HEDLEY_EMPTY_BASES
+#endif
+
+/* Remaining macros are deprecated. */
+
+#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)
+ #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#endif
+#if defined(__clang__)
+ #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)
+#else
+ #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
+#endif
+
+#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE)
+ #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)
+ #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN)
+ #undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#endif
+#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE)
+ #undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION)
+ #undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#endif
+#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)
+ #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#endif
+#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)
+
+#if defined(JSON_HEDLEY_CLANG_HAS_WARNING)
+ #undef JSON_HEDLEY_CLANG_HAS_WARNING
+#endif
+#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning)
+
+#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */
+
+
+// This file contains all internal macro definitions (except those affecting ABI)
+// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+// exclude unsupported compilers
+#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)
+ #if defined(__clang__)
+ #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400
+ #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers"
+ #endif
+ #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))
+ #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800
+ #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers"
+ #endif
+ #endif
+#endif
+
+// C++ language standard detection
+// if the user manually specified the used c++ version this is skipped
+#if !defined(JSON_HAS_CPP_23) && !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11)
+ #if (defined(__cplusplus) && __cplusplus > 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L)
+ #define JSON_HAS_CPP_23
+ #define JSON_HAS_CPP_20
+ #define JSON_HAS_CPP_17
+ #define JSON_HAS_CPP_14
+ #elif (defined(__cplusplus) && __cplusplus > 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L)
+ #define JSON_HAS_CPP_20
+ #define JSON_HAS_CPP_17
+ #define JSON_HAS_CPP_14
+ #elif (defined(__cplusplus) && __cplusplus > 201402L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
+ #define JSON_HAS_CPP_17
+ #define JSON_HAS_CPP_14
+ #elif (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
+ #define JSON_HAS_CPP_14
+ #endif
+ // the cpp 11 flag is always specified because it is the minimal required version
+ #define JSON_HAS_CPP_11
+#endif
+
+#ifdef __has_include
+ #if __has_include(<version>)
+ #include <version>
+ #endif
+#endif
+
+#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM)
+ #ifdef JSON_HAS_CPP_17
+ #if defined(__cpp_lib_filesystem)
+ #define JSON_HAS_FILESYSTEM 1
+ #elif defined(__cpp_lib_experimental_filesystem)
+ #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+ #elif !defined(__has_include)
+ #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+ #elif __has_include(<filesystem>)
+ #define JSON_HAS_FILESYSTEM 1
+ #elif __has_include(<experimental/filesystem>)
+ #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1
+ #endif
+
+ // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/
+ #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support
+ #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support
+ #if defined(__clang_major__) && __clang_major__ < 7
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support
+ #if defined(_MSC_VER) && _MSC_VER < 1914
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before iOS 13
+ #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+
+ // no filesystem support before macOS Catalina
+ #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #endif
+ #endif
+#endif
+
+#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_FILESYSTEM
+ #define JSON_HAS_FILESYSTEM 0
+#endif
+
+#ifndef JSON_HAS_THREE_WAY_COMPARISON
+ #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \
+ && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L
+ #define JSON_HAS_THREE_WAY_COMPARISON 1
+ #else
+ #define JSON_HAS_THREE_WAY_COMPARISON 0
+ #endif
+#endif
+
+#ifndef JSON_HAS_RANGES
+ // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error
+ #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427
+ #define JSON_HAS_RANGES 0
+ #elif defined(__cpp_lib_ranges)
+ #define JSON_HAS_RANGES 1
+ #else
+ #define JSON_HAS_RANGES 0
+ #endif
+#endif
+
+#ifndef JSON_HAS_STATIC_RTTI
+ #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0
+ #define JSON_HAS_STATIC_RTTI 1
+ #else
+ #define JSON_HAS_STATIC_RTTI 0
+ #endif
+#endif
+
+#ifdef JSON_HAS_CPP_17
+ #define JSON_INLINE_VARIABLE inline
+#else
+ #define JSON_INLINE_VARIABLE
+#endif
+
+#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address)
+ #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]]
+#else
+ #define JSON_NO_UNIQUE_ADDRESS
+#endif
+
+// disable documentation warnings on clang
+#if defined(__clang__)
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wdocumentation"
+ #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
+#endif
+
+// allow disabling exceptions
+#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)
+ #define JSON_THROW(exception) throw exception
+ #define JSON_TRY try
+ #define JSON_CATCH(exception) catch(exception)
+ #define JSON_INTERNAL_CATCH(exception) catch(exception)
+#else
+ #include <cstdlib>
+ #define JSON_THROW(exception) std::abort()
+ #define JSON_TRY if(true)
+ #define JSON_CATCH(exception) if(false)
+ #define JSON_INTERNAL_CATCH(exception) if(false)
+#endif
+
+// override exception macros
+#if defined(JSON_THROW_USER)
+ #undef JSON_THROW
+ #define JSON_THROW JSON_THROW_USER
+#endif
+#if defined(JSON_TRY_USER)
+ #undef JSON_TRY
+ #define JSON_TRY JSON_TRY_USER
+#endif
+#if defined(JSON_CATCH_USER)
+ #undef JSON_CATCH
+ #define JSON_CATCH JSON_CATCH_USER
+ #undef JSON_INTERNAL_CATCH
+ #define JSON_INTERNAL_CATCH JSON_CATCH_USER
+#endif
+#if defined(JSON_INTERNAL_CATCH_USER)
+ #undef JSON_INTERNAL_CATCH
+ #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER
+#endif
+
+// allow overriding assert
+#if !defined(JSON_ASSERT)
+ #include <cassert> // assert
+ #define JSON_ASSERT(x) assert(x)
+#endif
+
+// allow to access some private functions (needed by the test suite)
+#if defined(JSON_TESTS_PRIVATE)
+ #define JSON_PRIVATE_UNLESS_TESTED public
+#else
+ #define JSON_PRIVATE_UNLESS_TESTED private
+#endif
+
+/*!
+@brief macro to briefly define a mapping between an enum and JSON
+@def NLOHMANN_JSON_SERIALIZE_ENUM
+@since version 3.4.0
+*/
+#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \
+ template<typename BasicJsonType> \
+ inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \
+ { \
+ /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
+ static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
+ /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on <array> */ \
+ static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
+ auto it = std::find_if(std::begin(m), std::end(m), \
+ [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
+ { \
+ return ej_pair.first == e; \
+ }); \
+ j = ((it != std::end(m)) ? it : std::begin(m))->second; \
+ } \
+ template<typename BasicJsonType> \
+ inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \
+ { \
+ /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \
+ static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE " must be an enum!"); \
+ /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on <array> */ \
+ static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__; \
+ auto it = std::find_if(std::begin(m), std::end(m), \
+ [&j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \
+ { \
+ return ej_pair.second == j; \
+ }); \
+ e = ((it != std::end(m)) ? it : std::begin(m))->first; \
+ }
+
+// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
+// may be removed in the future once the class is split.
+
+#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \
+ template<template<typename, typename, typename...> class ObjectType, \
+ template<typename, typename...> class ArrayType, \
+ class StringType, class BooleanType, class NumberIntegerType, \
+ class NumberUnsignedType, class NumberFloatType, \
+ template<typename> class AllocatorType, \
+ template<typename, typename = void> class JSONSerializer, \
+ class BinaryType, \
+ class CustomBaseClass>
+
+#define NLOHMANN_BASIC_JSON_TPL \
+ basic_json<ObjectType, ArrayType, StringType, BooleanType, \
+ NumberIntegerType, NumberUnsignedType, NumberFloatType, \
+ AllocatorType, JSONSerializer, BinaryType, CustomBaseClass>
+
+// Macros to simplify conversion from/to types
+
+#define NLOHMANN_JSON_EXPAND( x ) x
+#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME
+#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \
+ NLOHMANN_JSON_PASTE64, \
+ NLOHMANN_JSON_PASTE63, \
+ NLOHMANN_JSON_PASTE62, \
+ NLOHMANN_JSON_PASTE61, \
+ NLOHMANN_JSON_PASTE60, \
+ NLOHMANN_JSON_PASTE59, \
+ NLOHMANN_JSON_PASTE58, \
+ NLOHMANN_JSON_PASTE57, \
+ NLOHMANN_JSON_PASTE56, \
+ NLOHMANN_JSON_PASTE55, \
+ NLOHMANN_JSON_PASTE54, \
+ NLOHMANN_JSON_PASTE53, \
+ NLOHMANN_JSON_PASTE52, \
+ NLOHMANN_JSON_PASTE51, \
+ NLOHMANN_JSON_PASTE50, \
+ NLOHMANN_JSON_PASTE49, \
+ NLOHMANN_JSON_PASTE48, \
+ NLOHMANN_JSON_PASTE47, \
+ NLOHMANN_JSON_PASTE46, \
+ NLOHMANN_JSON_PASTE45, \
+ NLOHMANN_JSON_PASTE44, \
+ NLOHMANN_JSON_PASTE43, \
+ NLOHMANN_JSON_PASTE42, \
+ NLOHMANN_JSON_PASTE41, \
+ NLOHMANN_JSON_PASTE40, \
+ NLOHMANN_JSON_PASTE39, \
+ NLOHMANN_JSON_PASTE38, \
+ NLOHMANN_JSON_PASTE37, \
+ NLOHMANN_JSON_PASTE36, \
+ NLOHMANN_JSON_PASTE35, \
+ NLOHMANN_JSON_PASTE34, \
+ NLOHMANN_JSON_PASTE33, \
+ NLOHMANN_JSON_PASTE32, \
+ NLOHMANN_JSON_PASTE31, \
+ NLOHMANN_JSON_PASTE30, \
+ NLOHMANN_JSON_PASTE29, \
+ NLOHMANN_JSON_PASTE28, \
+ NLOHMANN_JSON_PASTE27, \
+ NLOHMANN_JSON_PASTE26, \
+ NLOHMANN_JSON_PASTE25, \
+ NLOHMANN_JSON_PASTE24, \
+ NLOHMANN_JSON_PASTE23, \
+ NLOHMANN_JSON_PASTE22, \
+ NLOHMANN_JSON_PASTE21, \
+ NLOHMANN_JSON_PASTE20, \
+ NLOHMANN_JSON_PASTE19, \
+ NLOHMANN_JSON_PASTE18, \
+ NLOHMANN_JSON_PASTE17, \
+ NLOHMANN_JSON_PASTE16, \
+ NLOHMANN_JSON_PASTE15, \
+ NLOHMANN_JSON_PASTE14, \
+ NLOHMANN_JSON_PASTE13, \
+ NLOHMANN_JSON_PASTE12, \
+ NLOHMANN_JSON_PASTE11, \
+ NLOHMANN_JSON_PASTE10, \
+ NLOHMANN_JSON_PASTE9, \
+ NLOHMANN_JSON_PASTE8, \
+ NLOHMANN_JSON_PASTE7, \
+ NLOHMANN_JSON_PASTE6, \
+ NLOHMANN_JSON_PASTE5, \
+ NLOHMANN_JSON_PASTE4, \
+ NLOHMANN_JSON_PASTE3, \
+ NLOHMANN_JSON_PASTE2, \
+ NLOHMANN_JSON_PASTE1)(__VA_ARGS__))
+#define NLOHMANN_JSON_PASTE2(func, v1) func(v1)
+#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2)
+#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3)
+#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4)
+#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5)
+#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6)
+#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7)
+#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8)
+#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9)
+#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10)
+#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
+#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12)
+#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13)
+#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14)
+#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15)
+#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16)
+#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17)
+#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18)
+#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19)
+#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20)
+#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21)
+#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22)
+#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23)
+#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24)
+#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25)
+#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26)
+#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27)
+#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28)
+#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29)
+#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30)
+#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31)
+#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32)
+#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33)
+#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34)
+#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35)
+#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36)
+#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37)
+#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38)
+#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39)
+#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40)
+#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41)
+#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42)
+#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43)
+#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44)
+#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45)
+#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46)
+#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47)
+#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48)
+#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49)
+#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50)
+#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51)
+#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52)
+#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53)
+#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54)
+#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55)
+#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56)
+#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57)
+#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58)
+#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59)
+#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60)
+#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61)
+#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62)
+#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63)
+
+#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1;
+#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1);
+#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = !nlohmann_json_j.is_null() ? nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1) : nlohmann_json_default_obj.v1;
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_INTRUSIVE
+@since version 3.9.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
+*/
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT
+@since version 3.11.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
+*/
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE
+@since version 3.11.3
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/
+*/
+#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE
+@since version 3.9.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
+*/
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT
+@since version 3.11.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
+*/
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE
+@since version 3.11.3
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/
+*/
+#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE
+@since version 3.12.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
+*/
+#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT
+@since version 3.12.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
+*/
+#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE
+@since version 3.12.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
+*/
+#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE
+@since version 3.12.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
+*/
+#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT
+@since version 3.12.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
+*/
+#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast<BaseType&>(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) }
+
+/*!
+@brief macro
+@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE
+@since version 3.12.0
+@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/
+*/
+#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \
+ template<typename BasicJsonType, nlohmann::detail::enable_if_t<nlohmann::detail::is_basic_json<BasicJsonType>::value, int> = 0> \
+ void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast<const BaseType &>(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) }
+
+// inspired from https://stackoverflow.com/a/26745591
+// allows calling any std function as if (e.g., with begin):
+// using std::begin; begin(x);
+//
+// it allows using the detected idiom to retrieve the return type
+// of such an expression
+#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \
+ namespace detail { \
+ using std::std_name; \
+ \
+ template<typename... T> \
+ using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
+ } \
+ \
+ namespace detail2 { \
+ struct std_name##_tag \
+ { \
+ }; \
+ \
+ template<typename... T> \
+ std_name##_tag std_name(T&&...); \
+ \
+ template<typename... T> \
+ using result_of_##std_name = decltype(std_name(std::declval<T>()...)); \
+ \
+ template<typename... T> \
+ struct would_call_std_##std_name \
+ { \
+ static constexpr auto const value = ::nlohmann::detail:: \
+ is_detected_exact<std_name##_tag, result_of_##std_name, T...>::value; \
+ }; \
+ } /* namespace detail2 */ \
+ \
+ template<typename... T> \
+ struct would_call_std_##std_name : detail2::would_call_std_##std_name<T...> \
+ { \
+ }
+
+#ifndef JSON_USE_IMPLICIT_CONVERSIONS
+ #define JSON_USE_IMPLICIT_CONVERSIONS 1
+#endif
+
+#if JSON_USE_IMPLICIT_CONVERSIONS
+ #define JSON_EXPLICIT
+#else
+ #define JSON_EXPLICIT explicit
+#endif
+
+#ifndef JSON_DISABLE_ENUM_SERIALIZATION
+ #define JSON_DISABLE_ENUM_SERIALIZATION 0
+#endif
+
+#ifndef JSON_USE_GLOBAL_UDLS
+ #define JSON_USE_GLOBAL_UDLS 1
+#endif
+
+#if JSON_HAS_THREE_WAY_COMPARISON
+ #include <compare> // partial_ordering
+#endif
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////////////
+// JSON type enumeration //
+///////////////////////////
+
+/*!
+@brief the JSON type enumeration
+
+This enumeration collects the different JSON types. It is internally used to
+distinguish the stored values, and the functions @ref basic_json::is_null(),
+@ref basic_json::is_object(), @ref basic_json::is_array(),
+@ref basic_json::is_string(), @ref basic_json::is_boolean(),
+@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),
+@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),
+@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and
+@ref basic_json::is_structured() rely on it.
+
+@note There are three enumeration entries (number_integer, number_unsigned, and
+number_float), because the library distinguishes these three types for numbers:
+@ref basic_json::number_unsigned_t is used for unsigned integers,
+@ref basic_json::number_integer_t is used for signed integers, and
+@ref basic_json::number_float_t is used for floating-point numbers or to
+approximate integers which do not fit in the limits of their respective type.
+
+@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
+value with the default value for a given type
+
+@since version 1.0.0
+*/
+enum class value_t : std::uint8_t
+{
+ null, ///< null value
+ object, ///< object (unordered set of name/value pairs)
+ array, ///< array (ordered collection of values)
+ string, ///< string value
+ boolean, ///< boolean value
+ number_integer, ///< number value (signed integer)
+ number_unsigned, ///< number value (unsigned integer)
+ number_float, ///< number value (floating-point)
+ binary, ///< binary array (ordered collection of bytes)
+ discarded ///< discarded by the parser callback function
+};
+
+/*!
+@brief comparison operator for JSON types
+
+Returns an ordering that is similar to Python:
+- order: null < boolean < number < object < array < string < binary
+- furthermore, each type is not smaller than itself
+- discarded values are not comparable
+- binary is represented as a b"" string in python and directly comparable to a
+ string; however, making a binary array directly comparable with a string would
+ be surprising behavior in a JSON file.
+
+@since version 1.0.0
+*/
+#if JSON_HAS_THREE_WAY_COMPARISON
+ inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD*
+#else
+ inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+#endif
+{
+ static constexpr std::array<std::uint8_t, 9> order = {{
+ 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,
+ 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */,
+ 6 /* binary */
+ }
+ };
+
+ const auto l_index = static_cast<std::size_t>(lhs);
+ const auto r_index = static_cast<std::size_t>(rhs);
+#if JSON_HAS_THREE_WAY_COMPARISON
+ if (l_index < order.size() && r_index < order.size())
+ {
+ return order[l_index] <=> order[r_index]; // *NOPAD*
+ }
+ return std::partial_ordering::unordered;
+#else
+ return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index];
+#endif
+}
+
+// GCC selects the built-in operator< over an operator rewritten from
+// a user-defined spaceship operator
+// Clang, MSVC, and ICC select the rewritten candidate
+// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200)
+#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__)
+inline bool operator<(const value_t lhs, const value_t rhs) noexcept
+{
+ return std::is_lt(lhs <=> rhs); // *NOPAD*
+}
+#endif
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_escape.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief replace all occurrences of a substring by another string
+
+@param[in,out] s the string to manipulate; changed so that all
+ occurrences of @a f are replaced with @a t
+@param[in] f the substring to replace with @a t
+@param[in] t the string to replace @a f
+
+@pre The search string @a f must not be empty. **This precondition is
+enforced with an assertion.**
+
+@since version 2.0.0
+*/
+template<typename StringType>
+inline void replace_substring(StringType& s, const StringType& f,
+ const StringType& t)
+{
+ JSON_ASSERT(!f.empty());
+ for (auto pos = s.find(f); // find first occurrence of f
+ pos != StringType::npos; // make sure f was found
+ s.replace(pos, f.size(), t), // replace with t, and
+ pos = s.find(f, pos + t.size())) // find next occurrence of f
+ {}
+}
+
+/*!
+ * @brief string escaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to escape
+ * @return escaped string
+ *
+ * Note the order of escaping "~" to "~0" and "/" to "~1" is important.
+ */
+template<typename StringType>
+inline StringType escape(StringType s)
+{
+ replace_substring(s, StringType{"~"}, StringType{"~0"});
+ replace_substring(s, StringType{"/"}, StringType{"~1"});
+ return s;
+}
+
+/*!
+ * @brief string unescaping as described in RFC 6901 (Sect. 4)
+ * @param[in] s string to unescape
+ * @return unescaped string
+ *
+ * Note the order of escaping "~1" to "/" and "~0" to "~" is important.
+ */
+template<typename StringType>
+static void unescape(StringType& s)
+{
+ replace_substring(s, StringType{"~1"}, StringType{"/"});
+ replace_substring(s, StringType{"~0"}, StringType{"~"});
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/position_t.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // size_t
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// struct to capture the start position of the current token
+struct position_t
+{
+ /// the total number of characters read
+ std::size_t chars_read_total = 0;
+ /// the number of characters read in the current line
+ std::size_t chars_read_current_line = 0;
+ /// the number of lines read
+ std::size_t lines_read = 0;
+
+ /// conversion to size_t to preserve SAX interface
+ constexpr operator size_t() const
+ {
+ return chars_read_total;
+ }
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-FileCopyrightText: 2018 The Abseil Authors
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type
+#include <utility> // index_sequence, make_index_sequence, index_sequence_for
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
+
+#ifdef JSON_HAS_CPP_14
+
+// the following utilities are natively available in C++14
+using std::enable_if_t;
+using std::index_sequence;
+using std::make_index_sequence;
+using std::index_sequence_for;
+
+#else
+
+// alias templates to reduce boilerplate
+template<bool B, typename T = void>
+using enable_if_t = typename std::enable_if<B, T>::type;
+
+// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h
+// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0.
+
+//// START OF CODE FROM GOOGLE ABSEIL
+
+// integer_sequence
+//
+// Class template representing a compile-time integer sequence. An instantiation
+// of `integer_sequence<T, Ints...>` has a sequence of integers encoded in its
+// type through its template arguments (which is a common need when
+// working with C++11 variadic templates). `absl::integer_sequence` is designed
+// to be a drop-in replacement for C++14's `std::integer_sequence`.
+//
+// Example:
+//
+// template< class T, T... Ints >
+// void user_function(integer_sequence<T, Ints...>);
+//
+// int main()
+// {
+// // user_function's `T` will be deduced to `int` and `Ints...`
+// // will be deduced to `0, 1, 2, 3, 4`.
+// user_function(make_integer_sequence<int, 5>());
+// }
+template <typename T, T... Ints>
+struct integer_sequence
+{
+ using value_type = T;
+ static constexpr std::size_t size() noexcept
+ {
+ return sizeof...(Ints);
+ }
+};
+
+// index_sequence
+//
+// A helper template for an `integer_sequence` of `size_t`,
+// `absl::index_sequence` is designed to be a drop-in replacement for C++14's
+// `std::index_sequence`.
+template <size_t... Ints>
+using index_sequence = integer_sequence<size_t, Ints...>;
+
+namespace utility_internal
+{
+
+template <typename Seq, size_t SeqSize, size_t Rem>
+struct Extend;
+
+// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency.
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 0>
+{
+ using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >;
+};
+
+template <typename T, T... Ints, size_t SeqSize>
+struct Extend<integer_sequence<T, Ints...>, SeqSize, 1>
+{
+ using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >;
+};
+
+// Recursion helper for 'make_integer_sequence<T, N>'.
+// 'Gen<T, N>::type' is an alias for 'integer_sequence<T, 0, 1, ... N-1>'.
+template <typename T, size_t N>
+struct Gen
+{
+ using type =
+ typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type;
+};
+
+template <typename T>
+struct Gen<T, 0>
+{
+ using type = integer_sequence<T>;
+};
+
+} // namespace utility_internal
+
+// Compile-time sequences of integers
+
+// make_integer_sequence
+//
+// This template alias is equivalent to
+// `integer_sequence<int, 0, 1, ..., N-1>`, and is designed to be a drop-in
+// replacement for C++14's `std::make_integer_sequence`.
+template <typename T, T N>
+using make_integer_sequence = typename utility_internal::Gen<T, N>::type;
+
+// make_index_sequence
+//
+// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`,
+// and is designed to be a drop-in replacement for C++14's
+// `std::make_index_sequence`.
+template <size_t N>
+using make_index_sequence = make_integer_sequence<size_t, N>;
+
+// index_sequence_for
+//
+// Converts a typename pack into an index sequence of the same length, and
+// is designed to be a drop-in replacement for C++14's
+// `std::index_sequence_for()`
+template <typename... Ts>
+using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+
+//// END OF CODE FROM GOOGLE ABSEIL
+
+#endif
+
+// dispatch utility (taken from ranges-v3)
+template<unsigned N> struct priority_tag : priority_tag < N - 1 > {};
+template<> struct priority_tag<0> {};
+
+// taken from ranges-v3
+template<typename T>
+struct static_const
+{
+ static JSON_INLINE_VARIABLE constexpr T value{};
+};
+
+#ifndef JSON_HAS_CPP_17
+ template<typename T>
+ constexpr T static_const<T>::value;
+#endif
+
+template<typename T, typename... Args>
+constexpr std::array<T, sizeof...(Args)> make_array(Args&& ... args)
+{
+ return std::array<T, sizeof...(Args)> {{static_cast<T>(std::forward<Args>(args))...}};
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <limits> // numeric_limits
+#include <string> // char_traits
+#include <tuple> // tuple
+#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type
+#include <utility> // declval
+
+// #include <nlohmann/detail/iterators/iterator_traits.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <iterator> // random_access_iterator_tag
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/void_t.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename It, typename = void>
+struct iterator_types {};
+
+template<typename It>
+struct iterator_types <
+ It,
+ void_t<typename It::difference_type, typename It::value_type, typename It::pointer,
+ typename It::reference, typename It::iterator_category >>
+{
+ using difference_type = typename It::difference_type;
+ using value_type = typename It::value_type;
+ using pointer = typename It::pointer;
+ using reference = typename It::reference;
+ using iterator_category = typename It::iterator_category;
+};
+
+// This is required as some compilers implement std::iterator_traits in a way that
+// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341.
+template<typename T, typename = void>
+struct iterator_traits
+{
+};
+
+template<typename T>
+struct iterator_traits < T, enable_if_t < !std::is_pointer<T>::value >>
+ : iterator_types<T>
+{
+};
+
+template<typename T>
+struct iterator_traits<T*, enable_if_t<std::is_object<T>::value>>
+{
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = T;
+ using difference_type = ptrdiff_t;
+ using pointer = T*;
+ using reference = T&;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/call_std/begin.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin);
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/call_std/end.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end);
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+// #include <nlohmann/json_fwd.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
+ #define INCLUDE_NLOHMANN_JSON_FWD_HPP_
+
+ #include <cstdint> // int64_t, uint64_t
+ #include <map> // map
+ #include <memory> // allocator
+ #include <string> // string
+ #include <vector> // vector
+
+ // #include <nlohmann/detail/abi_macros.hpp>
+
+
+ /*!
+ @brief namespace for Niels Lohmann
+ @see https://github.com/nlohmann
+ @since version 1.0.0
+ */
+ NLOHMANN_JSON_NAMESPACE_BEGIN
+
+ /*!
+ @brief default JSONSerializer template argument
+
+ This serializer ignores the template arguments and uses ADL
+ ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
+ for serialization.
+ */
+ template<typename T = void, typename SFINAE = void>
+ struct adl_serializer;
+
+ /// a class to store JSON values
+ /// @sa https://json.nlohmann.me/api/basic_json/
+ template<template<typename U, typename V, typename... Args> class ObjectType =
+ std::map,
+ template<typename U, typename... Args> class ArrayType = std::vector,
+ class StringType = std::string, class BooleanType = bool,
+ class NumberIntegerType = std::int64_t,
+ class NumberUnsignedType = std::uint64_t,
+ class NumberFloatType = double,
+ template<typename U> class AllocatorType = std::allocator,
+ template<typename T, typename SFINAE = void> class JSONSerializer =
+ adl_serializer,
+ class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
+ class CustomBaseClass = void>
+ class basic_json;
+
+ /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+ /// @sa https://json.nlohmann.me/api/json_pointer/
+ template<typename RefStringType>
+ class json_pointer;
+
+ /*!
+ @brief default specialization
+ @sa https://json.nlohmann.me/api/json/
+ */
+ using json = basic_json<>;
+
+ /// @brief a minimal map-like container that preserves insertion order
+ /// @sa https://json.nlohmann.me/api/ordered_map/
+ template<class Key, class T, class IgnoredLess, class Allocator>
+ struct ordered_map;
+
+ /// @brief specialization that maintains the insertion order of object keys
+ /// @sa https://json.nlohmann.me/api/ordered_json/
+ using ordered_json = basic_json<nlohmann::ordered_map>;
+
+ NLOHMANN_JSON_NAMESPACE_END
+
+#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+/*!
+@brief detail namespace with internal helper functions
+
+This namespace collects functions that should not be exposed,
+implementations of some @ref basic_json methods, and meta-programming helpers.
+
+@since version 2.1.0
+*/
+namespace detail
+{
+
+/////////////
+// helpers //
+/////////////
+
+// Note to maintainers:
+//
+// Every trait in this file expects a non CV-qualified type.
+// The only exceptions are in the 'aliases for detected' section
+// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))
+//
+// In this case, T has to be properly CV-qualified to constraint the function arguments
+// (e.g. to_json(BasicJsonType&, const T&))
+
+template<typename> struct is_basic_json : std::false_type {};
+
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};
+
+// used by exceptions create() member functions
+// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t
+// false_type otherwise
+template<typename BasicJsonContext>
+struct is_basic_json_context :
+ std::integral_constant < bool,
+ is_basic_json<typename std::remove_cv<typename std::remove_pointer<BasicJsonContext>::type>::type>::value
+ || std::is_same<BasicJsonContext, std::nullptr_t>::value >
+{};
+
+//////////////////////
+// json_ref helpers //
+//////////////////////
+
+template<typename>
+class json_ref;
+
+template<typename>
+struct is_json_ref : std::false_type {};
+
+template<typename T>
+struct is_json_ref<json_ref<T>> : std::true_type {};
+
+//////////////////////////
+// aliases for detected //
+//////////////////////////
+
+template<typename T>
+using mapped_type_t = typename T::mapped_type;
+
+template<typename T>
+using key_type_t = typename T::key_type;
+
+template<typename T>
+using value_type_t = typename T::value_type;
+
+template<typename T>
+using difference_type_t = typename T::difference_type;
+
+template<typename T>
+using pointer_t = typename T::pointer;
+
+template<typename T>
+using reference_t = typename T::reference;
+
+template<typename T>
+using iterator_category_t = typename T::iterator_category;
+
+template<typename T, typename... Args>
+using to_json_function = decltype(T::to_json(std::declval<Args>()...));
+
+template<typename T, typename... Args>
+using from_json_function = decltype(T::from_json(std::declval<Args>()...));
+
+template<typename T, typename U>
+using get_template_function = decltype(std::declval<T>().template get<U>());
+
+// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists
+template<typename BasicJsonType, typename T, typename = void>
+struct has_from_json : std::false_type {};
+
+// trait checking if j.get<T> is valid
+// use this trait instead of std::is_constructible or std::is_convertible,
+// both rely on, or make use of implicit conversions, and thus fail when T
+// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958)
+template <typename BasicJsonType, typename T>
+struct is_getable
+{
+ static constexpr bool value = is_detected<get_template_function, const BasicJsonType&, T>::value;
+};
+
+template<typename BasicJsonType, typename T>
+struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+ using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+ static constexpr bool value =
+ is_detected_exact<void, from_json_function, serializer,
+ const BasicJsonType&, T&>::value;
+};
+
+// This trait checks if JSONSerializer<T>::from_json(json const&) exists
+// this overload is used for non-default-constructible user-defined-types
+template<typename BasicJsonType, typename T, typename = void>
+struct has_non_default_from_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+ using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+ static constexpr bool value =
+ is_detected_exact<T, from_json_function, serializer,
+ const BasicJsonType&>::value;
+};
+
+// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
+// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
+template<typename BasicJsonType, typename T, typename = void>
+struct has_to_json : std::false_type {};
+
+template<typename BasicJsonType, typename T>
+struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json<T>::value >>
+{
+ using serializer = typename BasicJsonType::template json_serializer<T, void>;
+
+ static constexpr bool value =
+ is_detected_exact<void, to_json_function, serializer, BasicJsonType&,
+ T>::value;
+};
+
+template<typename T>
+using detect_key_compare = typename T::key_compare;
+
+template<typename T>
+struct has_key_compare : std::integral_constant<bool, is_detected<detect_key_compare, T>::value> {};
+
+// obtains the actual object key comparator
+template<typename BasicJsonType>
+struct actual_object_comparator
+{
+ using object_t = typename BasicJsonType::object_t;
+ using object_comparator_t = typename BasicJsonType::default_object_comparator_t;
+ using type = typename std::conditional < has_key_compare<object_t>::value,
+ typename object_t::key_compare, object_comparator_t>::type;
+};
+
+template<typename BasicJsonType>
+using actual_object_comparator_t = typename actual_object_comparator<BasicJsonType>::type;
+
+/////////////////
+// char_traits //
+/////////////////
+
+// Primary template of char_traits calls std char_traits
+template<typename T>
+struct char_traits : std::char_traits<T>
+{};
+
+// Explicitly define char traits for unsigned char since it is not standard
+template<>
+struct char_traits<unsigned char> : std::char_traits<char>
+{
+ using char_type = unsigned char;
+ using int_type = uint64_t;
+
+ // Redefine to_int_type function
+ static int_type to_int_type(char_type c) noexcept
+ {
+ return static_cast<int_type>(c);
+ }
+
+ static char_type to_char_type(int_type i) noexcept
+ {
+ return static_cast<char_type>(i);
+ }
+
+ static constexpr int_type eof() noexcept
+ {
+ return static_cast<int_type>(std::char_traits<char>::eof());
+ }
+};
+
+// Explicitly define char traits for signed char since it is not standard
+template<>
+struct char_traits<signed char> : std::char_traits<char>
+{
+ using char_type = signed char;
+ using int_type = uint64_t;
+
+ // Redefine to_int_type function
+ static int_type to_int_type(char_type c) noexcept
+ {
+ return static_cast<int_type>(c);
+ }
+
+ static char_type to_char_type(int_type i) noexcept
+ {
+ return static_cast<char_type>(i);
+ }
+
+ static constexpr int_type eof() noexcept
+ {
+ return static_cast<int_type>(std::char_traits<char>::eof());
+ }
+};
+
+///////////////////
+// is_ functions //
+///////////////////
+
+// https://en.cppreference.com/w/cpp/types/conjunction
+template<class...> struct conjunction : std::true_type { };
+template<class B> struct conjunction<B> : B { };
+template<class B, class... Bn>
+struct conjunction<B, Bn...>
+: std::conditional<static_cast<bool>(B::value), conjunction<Bn...>, B>::type {};
+
+// https://en.cppreference.com/w/cpp/types/negation
+template<class B> struct negation : std::integral_constant < bool, !B::value > { };
+
+// Reimplementation of is_constructible and is_default_constructible, due to them being broken for
+// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367).
+// This causes compile errors in e.g. clang 3.5 or gcc 4.9.
+template <typename T>
+struct is_default_constructible : std::is_default_constructible<T> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<std::pair<T1, T2>>
+ : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename T1, typename T2>
+struct is_default_constructible<const std::pair<T1, T2>>
+ : conjunction<is_default_constructible<T1>, is_default_constructible<T2>> {};
+
+template <typename... Ts>
+struct is_default_constructible<std::tuple<Ts...>>
+ : conjunction<is_default_constructible<Ts>...> {};
+
+template <typename... Ts>
+struct is_default_constructible<const std::tuple<Ts...>>
+ : conjunction<is_default_constructible<Ts>...> {};
+
+template <typename T, typename... Args>
+struct is_constructible : std::is_constructible<T, Args...> {};
+
+template <typename T1, typename T2>
+struct is_constructible<std::pair<T1, T2>> : is_default_constructible<std::pair<T1, T2>> {};
+
+template <typename T1, typename T2>
+struct is_constructible<const std::pair<T1, T2>> : is_default_constructible<const std::pair<T1, T2>> {};
+
+template <typename... Ts>
+struct is_constructible<std::tuple<Ts...>> : is_default_constructible<std::tuple<Ts...>> {};
+
+template <typename... Ts>
+struct is_constructible<const std::tuple<Ts...>> : is_default_constructible<const std::tuple<Ts...>> {};
+
+template<typename T, typename = void>
+struct is_iterator_traits : std::false_type {};
+
+template<typename T>
+struct is_iterator_traits<iterator_traits<T>>
+{
+ private:
+ using traits = iterator_traits<T>;
+
+ public:
+ static constexpr auto value =
+ is_detected<value_type_t, traits>::value &&
+ is_detected<difference_type_t, traits>::value &&
+ is_detected<pointer_t, traits>::value &&
+ is_detected<iterator_category_t, traits>::value &&
+ is_detected<reference_t, traits>::value;
+};
+
+template<typename T>
+struct is_range
+{
+ private:
+ using t_ref = typename std::add_lvalue_reference<T>::type;
+
+ using iterator = detected_t<result_of_begin, t_ref>;
+ using sentinel = detected_t<result_of_end, t_ref>;
+
+ // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator
+ // and https://en.cppreference.com/w/cpp/iterator/sentinel_for
+ // but reimplementing these would be too much work, as a lot of other concepts are used underneath
+ static constexpr auto is_iterator_begin =
+ is_iterator_traits<iterator_traits<iterator>>::value;
+
+ public:
+ static constexpr bool value = !std::is_same<iterator, nonesuch>::value && !std::is_same<sentinel, nonesuch>::value && is_iterator_begin;
+};
+
+template<typename R>
+using iterator_t = enable_if_t<is_range<R>::value, result_of_begin<decltype(std::declval<R&>())>>;
+
+template<typename T>
+using range_value_t = value_type_t<iterator_traits<iterator_t<T>>>;
+
+// The following implementation of is_complete_type is taken from
+// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
+// and is written by Xiang Fan who agreed to using it in this library.
+
+template<typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template<typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType,
+ typename = void>
+struct is_compatible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type_impl <
+ BasicJsonType, CompatibleObjectType,
+ enable_if_t < is_detected<mapped_type_t, CompatibleObjectType>::value&&
+ is_detected<key_type_t, CompatibleObjectType>::value >>
+{
+ using object_t = typename BasicJsonType::object_t;
+
+ // macOS's is_constructible does not play well with nonesuch...
+ static constexpr bool value =
+ is_constructible<typename object_t::key_type,
+ typename CompatibleObjectType::key_type>::value &&
+ is_constructible<typename object_t::mapped_type,
+ typename CompatibleObjectType::mapped_type>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleObjectType>
+struct is_compatible_object_type
+ : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+ typename = void>
+struct is_constructible_object_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type_impl <
+ BasicJsonType, ConstructibleObjectType,
+ enable_if_t < is_detected<mapped_type_t, ConstructibleObjectType>::value&&
+ is_detected<key_type_t, ConstructibleObjectType>::value >>
+{
+ using object_t = typename BasicJsonType::object_t;
+
+ static constexpr bool value =
+ (is_default_constructible<ConstructibleObjectType>::value &&
+ (std::is_move_assignable<ConstructibleObjectType>::value ||
+ std::is_copy_assignable<ConstructibleObjectType>::value) &&
+ (is_constructible<typename ConstructibleObjectType::key_type,
+ typename object_t::key_type>::value &&
+ std::is_same <
+ typename object_t::mapped_type,
+ typename ConstructibleObjectType::mapped_type >::value)) ||
+ (has_from_json<BasicJsonType,
+ typename ConstructibleObjectType::mapped_type>::value ||
+ has_non_default_from_json <
+ BasicJsonType,
+ typename ConstructibleObjectType::mapped_type >::value);
+};
+
+template<typename BasicJsonType, typename ConstructibleObjectType>
+struct is_constructible_object_type
+ : is_constructible_object_type_impl<BasicJsonType,
+ ConstructibleObjectType> {};
+
+template<typename BasicJsonType, typename CompatibleStringType>
+struct is_compatible_string_type
+{
+ static constexpr auto value =
+ is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;
+};
+
+template<typename BasicJsonType, typename ConstructibleStringType>
+struct is_constructible_string_type
+{
+ // launder type through decltype() to fix compilation failure on ICPC
+#ifdef __INTEL_COMPILER
+ using laundered_type = decltype(std::declval<ConstructibleStringType>());
+#else
+ using laundered_type = ConstructibleStringType;
+#endif
+
+ static constexpr auto value =
+ conjunction <
+ is_constructible<laundered_type, typename BasicJsonType::string_t>,
+ is_detected_exact<typename BasicJsonType::string_t::value_type,
+ value_type_t, laundered_type >>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType, typename = void>
+struct is_compatible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type_impl <
+ BasicJsonType, CompatibleArrayType,
+ enable_if_t <
+ is_detected<iterator_t, CompatibleArrayType>::value&&
+ is_iterator_traits<iterator_traits<detected_t<iterator_t, CompatibleArrayType>>>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+ !std::is_same<CompatibleArrayType, detected_t<range_value_t, CompatibleArrayType>>::value >>
+{
+ static constexpr bool value =
+ is_constructible<BasicJsonType,
+ range_value_t<CompatibleArrayType>>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleArrayType>
+struct is_compatible_array_type
+ : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType, typename = void>
+struct is_constructible_array_type_impl : std::false_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+ BasicJsonType, ConstructibleArrayType,
+ enable_if_t<std::is_same<ConstructibleArrayType,
+ typename BasicJsonType::value_type>::value >>
+ : std::true_type {};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type_impl <
+ BasicJsonType, ConstructibleArrayType,
+ enable_if_t < !std::is_same<ConstructibleArrayType,
+ typename BasicJsonType::value_type>::value&&
+ !is_compatible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+ is_default_constructible<ConstructibleArrayType>::value&&
+(std::is_move_assignable<ConstructibleArrayType>::value ||
+ std::is_copy_assignable<ConstructibleArrayType>::value)&&
+is_detected<iterator_t, ConstructibleArrayType>::value&&
+is_iterator_traits<iterator_traits<detected_t<iterator_t, ConstructibleArrayType>>>::value&&
+is_detected<range_value_t, ConstructibleArrayType>::value&&
+// special case for types like std::filesystem::path whose iterator's value_type are themselves
+// c.f. https://github.com/nlohmann/json/pull/3073
+!std::is_same<ConstructibleArrayType, detected_t<range_value_t, ConstructibleArrayType>>::value&&
+is_complete_type <
+detected_t<range_value_t, ConstructibleArrayType >>::value >>
+{
+ using value_type = range_value_t<ConstructibleArrayType>;
+
+ static constexpr bool value =
+ std::is_same<value_type,
+ typename BasicJsonType::array_t::value_type>::value ||
+ has_from_json<BasicJsonType,
+ value_type>::value ||
+ has_non_default_from_json <
+ BasicJsonType,
+ value_type >::value;
+};
+
+template<typename BasicJsonType, typename ConstructibleArrayType>
+struct is_constructible_array_type
+ : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType,
+ typename = void>
+struct is_compatible_integer_type_impl : std::false_type {};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type_impl <
+ RealIntegerType, CompatibleNumberIntegerType,
+ enable_if_t < std::is_integral<RealIntegerType>::value&&
+ std::is_integral<CompatibleNumberIntegerType>::value&&
+ !std::is_same<bool, CompatibleNumberIntegerType>::value >>
+{
+ // is there an assert somewhere on overflows?
+ using RealLimits = std::numeric_limits<RealIntegerType>;
+ using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;
+
+ static constexpr auto value =
+ is_constructible<RealIntegerType,
+ CompatibleNumberIntegerType>::value &&
+ CompatibleLimits::is_integer &&
+ RealLimits::is_signed == CompatibleLimits::is_signed;
+};
+
+template<typename RealIntegerType, typename CompatibleNumberIntegerType>
+struct is_compatible_integer_type
+ : is_compatible_integer_type_impl<RealIntegerType,
+ CompatibleNumberIntegerType> {};
+
+template<typename BasicJsonType, typename CompatibleType, typename = void>
+struct is_compatible_type_impl: std::false_type {};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type_impl <
+ BasicJsonType, CompatibleType,
+ enable_if_t<is_complete_type<CompatibleType>::value >>
+{
+ static constexpr bool value =
+ has_to_json<BasicJsonType, CompatibleType>::value;
+};
+
+template<typename BasicJsonType, typename CompatibleType>
+struct is_compatible_type
+ : is_compatible_type_impl<BasicJsonType, CompatibleType> {};
+
+template<typename T1, typename T2>
+struct is_constructible_tuple : std::false_type {};
+
+template<typename T1, typename... Args>
+struct is_constructible_tuple<T1, std::tuple<Args...>> : conjunction<is_constructible<T1, Args>...> {};
+
+template<typename BasicJsonType, typename T>
+struct is_json_iterator_of : std::false_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::iterator> : std::true_type {};
+
+template<typename BasicJsonType>
+struct is_json_iterator_of<BasicJsonType, typename BasicJsonType::const_iterator> : std::true_type
+{};
+
+// checks if a given type T is a template specialization of Primary
+template<template <typename...> class Primary, typename T>
+struct is_specialization_of : std::false_type {};
+
+template<template <typename...> class Primary, typename... Args>
+struct is_specialization_of<Primary, Primary<Args...>> : std::true_type {};
+
+template<typename T>
+using is_json_pointer = is_specialization_of<::nlohmann::json_pointer, uncvref_t<T>>;
+
+// checks if A and B are comparable using Compare functor
+template<typename Compare, typename A, typename B, typename = void>
+struct is_comparable : std::false_type {};
+
+template<typename Compare, typename A, typename B>
+struct is_comparable<Compare, A, B, void_t<
+decltype(std::declval<Compare>()(std::declval<A>(), std::declval<B>())),
+decltype(std::declval<Compare>()(std::declval<B>(), std::declval<A>()))
+>> : std::true_type {};
+
+template<typename T>
+using detect_is_transparent = typename T::is_transparent;
+
+// type trait to check if KeyType can be used as object key (without a BasicJsonType)
+// see is_usable_as_basic_json_key_type below
+template<typename Comparator, typename ObjectKeyType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+ bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_key_type = typename std::conditional <
+ is_comparable<Comparator, ObjectKeyType, KeyTypeCVRef>::value
+ && !(ExcludeObjectKeyType && std::is_same<KeyType,
+ ObjectKeyType>::value)
+ && (!RequireTransparentComparator
+ || is_detected <detect_is_transparent, Comparator>::value)
+ && !is_json_pointer<KeyType>::value,
+ std::true_type,
+ std::false_type >::type;
+
+// type trait to check if KeyType can be used as object key
+// true if:
+// - KeyType is comparable with BasicJsonType::object_t::key_type
+// - if ExcludeObjectKeyType is true, KeyType is not BasicJsonType::object_t::key_type
+// - the comparator is transparent or RequireTransparentComparator is false
+// - KeyType is not a JSON iterator or json_pointer
+template<typename BasicJsonType, typename KeyTypeCVRef, bool RequireTransparentComparator = true,
+ bool ExcludeObjectKeyType = RequireTransparentComparator, typename KeyType = uncvref_t<KeyTypeCVRef>>
+using is_usable_as_basic_json_key_type = typename std::conditional <
+ is_usable_as_key_type<typename BasicJsonType::object_comparator_t,
+ typename BasicJsonType::object_t::key_type, KeyTypeCVRef,
+ RequireTransparentComparator, ExcludeObjectKeyType>::value
+ && !is_json_iterator_of<BasicJsonType, KeyType>::value,
+ std::true_type,
+ std::false_type >::type;
+
+template<typename ObjectType, typename KeyType>
+using detect_erase_with_key_type = decltype(std::declval<ObjectType&>().erase(std::declval<KeyType>()));
+
+// type trait to check if object_t has an erase() member functions accepting KeyType
+template<typename BasicJsonType, typename KeyType>
+using has_erase_with_key_type = typename std::conditional <
+ is_detected <
+ detect_erase_with_key_type,
+ typename BasicJsonType::object_t, KeyType >::value,
+ std::true_type,
+ std::false_type >::type;
+
+// a naive helper to check if a type is an ordered_map (exploits the fact that
+// ordered_map inherits capacity() from std::vector)
+template <typename T>
+struct is_ordered_map
+{
+ using one = char;
+
+ struct two
+ {
+ char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ };
+
+ template <typename C> static one test( decltype(&C::capacity) ) ;
+ template <typename C> static two test(...);
+
+ enum { value = sizeof(test<T>(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+};
+
+// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
+template < typename T, typename U, enable_if_t < !std::is_same<T, U>::value, int > = 0 >
+T conditional_static_cast(U value)
+{
+ return static_cast<T>(value);
+}
+
+template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
+T conditional_static_cast(U value)
+{
+ return value;
+}
+
+template<typename... Types>
+using all_integral = conjunction<std::is_integral<Types>...>;
+
+template<typename... Types>
+using all_signed = conjunction<std::is_signed<Types>...>;
+
+template<typename... Types>
+using all_unsigned = conjunction<std::is_unsigned<Types>...>;
+
+// there's a disjunction trait in another PR; replace when merged
+template<typename... Types>
+using same_sign = std::integral_constant < bool,
+ all_signed<Types...>::value || all_unsigned<Types...>::value >;
+
+template<typename OfType, typename T>
+using never_out_of_range = std::integral_constant < bool,
+ (std::is_signed<OfType>::value && (sizeof(T) < sizeof(OfType)))
+ || (same_sign<OfType, T>::value && sizeof(OfType) == sizeof(T)) >;
+
+template<typename OfType, typename T,
+ bool OfTypeSigned = std::is_signed<OfType>::value,
+ bool TSigned = std::is_signed<T>::value>
+struct value_in_range_of_impl2;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, false>
+{
+ static constexpr bool test(T val)
+ {
+ using CommonType = typename std::common_type<OfType, T>::type;
+ return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+ }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, false>
+{
+ static constexpr bool test(T val)
+ {
+ using CommonType = typename std::common_type<OfType, T>::type;
+ return static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+ }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, false, true>
+{
+ static constexpr bool test(T val)
+ {
+ using CommonType = typename std::common_type<OfType, T>::type;
+ return val >= 0 && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+ }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl2<OfType, T, true, true>
+{
+ static constexpr bool test(T val)
+ {
+ using CommonType = typename std::common_type<OfType, T>::type;
+ return static_cast<CommonType>(val) >= static_cast<CommonType>((std::numeric_limits<OfType>::min)())
+ && static_cast<CommonType>(val) <= static_cast<CommonType>((std::numeric_limits<OfType>::max)());
+ }
+};
+
+template<typename OfType, typename T,
+ bool NeverOutOfRange = never_out_of_range<OfType, T>::value,
+ typename = detail::enable_if_t<all_integral<OfType, T>::value>>
+struct value_in_range_of_impl1;
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, false>
+{
+ static constexpr bool test(T val)
+ {
+ return value_in_range_of_impl2<OfType, T>::test(val);
+ }
+};
+
+template<typename OfType, typename T>
+struct value_in_range_of_impl1<OfType, T, true>
+{
+ static constexpr bool test(T /*val*/)
+ {
+ return true;
+ }
+};
+
+template<typename OfType, typename T>
+constexpr bool value_in_range_of(T val)
+{
+ return value_in_range_of_impl1<OfType, T>::test(val);
+}
+
+template<bool Value>
+using bool_constant = std::integral_constant<bool, Value>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_c_string
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+constexpr bool is_c_string()
+{
+ using TUnExt = typename std::remove_extent<T>::type;
+ using TUnCVExt = typename std::remove_cv<TUnExt>::type;
+ using TUnPtr = typename std::remove_pointer<T>::type;
+ using TUnCVPtr = typename std::remove_cv<TUnPtr>::type;
+ return
+ (std::is_array<T>::value && std::is_same<TUnCVExt, char>::value)
+ || (std::is_pointer<T>::value && std::is_same<TUnCVPtr, char>::value);
+}
+
+} // namespace impl
+
+// checks whether T is a [cv] char */[cv] char[] C string
+template<typename T>
+struct is_c_string : bool_constant<impl::is_c_string<T>()> {};
+
+template<typename T>
+using is_c_string_uncvref = is_c_string<uncvref_t<T>>;
+
+///////////////////////////////////////////////////////////////////////////////
+// is_transparent
+///////////////////////////////////////////////////////////////////////////////
+
+namespace impl
+{
+
+template<typename T>
+constexpr bool is_transparent()
+{
+ return is_detected<detect_is_transparent, T>::value;
+}
+
+} // namespace impl
+
+// checks whether T has a member named is_transparent
+template<typename T>
+struct is_transparent : bool_constant<impl::is_transparent<T>()> {};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_concat.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstring> // strlen
+#include <string> // string
+#include <utility> // forward
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+inline std::size_t concat_length()
+{
+ return 0;
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, const Args& ... rest);
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, const Args& ... rest);
+
+template<typename... Args>
+inline std::size_t concat_length(const char /*c*/, const Args& ... rest)
+{
+ return 1 + concat_length(rest...);
+}
+
+template<typename... Args>
+inline std::size_t concat_length(const char* cstr, const Args& ... rest)
+{
+ // cppcheck-suppress ignoredReturnValue
+ return ::strlen(cstr) + concat_length(rest...);
+}
+
+template<typename StringType, typename... Args>
+inline std::size_t concat_length(const StringType& str, const Args& ... rest)
+{
+ return str.size() + concat_length(rest...);
+}
+
+template<typename OutStringType>
+inline void concat_into(OutStringType& /*out*/)
+{}
+
+template<typename StringType, typename Arg>
+using string_can_append = decltype(std::declval<StringType&>().append(std::declval < Arg && > ()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append = is_detected<string_can_append, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_op = decltype(std::declval<StringType&>() += std::declval < Arg && > ());
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_op = is_detected<string_can_append_op, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_iter = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().begin(), std::declval<const Arg&>().end()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_iter = is_detected<string_can_append_iter, StringType, Arg>;
+
+template<typename StringType, typename Arg>
+using string_can_append_data = decltype(std::declval<StringType&>().append(std::declval<const Arg&>().data(), std::declval<const Arg&>().size()));
+
+template<typename StringType, typename Arg>
+using detect_string_can_append_data = is_detected<string_can_append_data, StringType, Arg>;
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && detect_string_can_append_op<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && !detect_string_can_append_op<OutStringType, Arg>::value
+ && detect_string_can_append_iter<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && !detect_string_can_append_op<OutStringType, Arg>::value
+ && !detect_string_can_append_iter<OutStringType, Arg>::value
+ && detect_string_can_append_data<OutStringType, Arg>::value, int > = 0 >
+inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest);
+
+template<typename OutStringType, typename Arg, typename... Args,
+ enable_if_t<detect_string_can_append<OutStringType, Arg>::value, int> = 0>
+inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest)
+{
+ out.append(std::forward<Arg>(arg));
+ concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && detect_string_can_append_op<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest)
+{
+ out += std::forward<Arg>(arg);
+ concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && !detect_string_can_append_op<OutStringType, Arg>::value
+ && detect_string_can_append_iter<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+ out.append(arg.begin(), arg.end());
+ concat_into(out, std::forward<Args>(rest)...);
+}
+
+template < typename OutStringType, typename Arg, typename... Args,
+ enable_if_t < !detect_string_can_append<OutStringType, Arg>::value
+ && !detect_string_can_append_op<OutStringType, Arg>::value
+ && !detect_string_can_append_iter<OutStringType, Arg>::value
+ && detect_string_can_append_data<OutStringType, Arg>::value, int > >
+inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest)
+{
+ out.append(arg.data(), arg.size());
+ concat_into(out, std::forward<Args>(rest)...);
+}
+
+template<typename OutStringType = std::string, typename... Args>
+inline OutStringType concat(Args && ... args)
+{
+ OutStringType str;
+ str.reserve(concat_length(args...));
+ concat_into(str, std::forward<Args>(args)...);
+ return str;
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+// With -Wweak-vtables, Clang will complain about the exception classes as they
+// have no out-of-line virtual method definitions and their vtable will be
+// emitted in every translation unit. This issue cannot be fixed with a
+// header-only library as there is no implementation file to move these
+// functions to. As a result, we suppress this warning here to avoid client
+// code to stumble over this. See https://github.com/nlohmann/json/issues/4087
+// for a discussion.
+#if defined(__clang__)
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+////////////////
+// exceptions //
+////////////////
+
+/// @brief general exception of the @ref basic_json class
+/// @sa https://json.nlohmann.me/api/basic_json/exception/
+class exception : public std::exception
+{
+ public:
+ /// returns the explanatory string
+ const char* what() const noexcept override
+ {
+ return m.what();
+ }
+
+ /// the id of the exception
+ const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
+
+ protected:
+ JSON_HEDLEY_NON_NULL(3)
+ exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
+
+ static std::string name(const std::string& ename, int id_)
+ {
+ return concat("[json.exception.", ename, '.', std::to_string(id_), "] ");
+ }
+
+ static std::string diagnostics(std::nullptr_t /*leaf_element*/)
+ {
+ return "";
+ }
+
+ template<typename BasicJsonType>
+ static std::string diagnostics(const BasicJsonType* leaf_element)
+ {
+#if JSON_DIAGNOSTICS
+ std::vector<std::string> tokens;
+ for (const auto* current = leaf_element; current != nullptr && current->m_parent != nullptr; current = current->m_parent)
+ {
+ switch (current->m_parent->type())
+ {
+ case value_t::array:
+ {
+ for (std::size_t i = 0; i < current->m_parent->m_data.m_value.array->size(); ++i)
+ {
+ if (&current->m_parent->m_data.m_value.array->operator[](i) == current)
+ {
+ tokens.emplace_back(std::to_string(i));
+ break;
+ }
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ for (const auto& element : *current->m_parent->m_data.m_value.object)
+ {
+ if (&element.second == current)
+ {
+ tokens.emplace_back(element.first.c_str());
+ break;
+ }
+ }
+ break;
+ }
+
+ case value_t::null: // LCOV_EXCL_LINE
+ case value_t::string: // LCOV_EXCL_LINE
+ case value_t::boolean: // LCOV_EXCL_LINE
+ case value_t::number_integer: // LCOV_EXCL_LINE
+ case value_t::number_unsigned: // LCOV_EXCL_LINE
+ case value_t::number_float: // LCOV_EXCL_LINE
+ case value_t::binary: // LCOV_EXCL_LINE
+ case value_t::discarded: // LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ break; // LCOV_EXCL_LINE
+ }
+ }
+
+ if (tokens.empty())
+ {
+ return "";
+ }
+
+ auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
+ [](const std::string & a, const std::string & b)
+ {
+ return concat(a, '/', detail::escape(b));
+ });
+
+ return concat('(', str, ") ", get_byte_positions(leaf_element));
+#else
+ return get_byte_positions(leaf_element);
+#endif
+ }
+
+ private:
+ /// an exception object as storage for error messages
+ std::runtime_error m;
+#if JSON_DIAGNOSTIC_POSITIONS
+ template<typename BasicJsonType>
+ static std::string get_byte_positions(const BasicJsonType* leaf_element)
+ {
+ if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))
+ {
+ return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
+ }
+ return "";
+ }
+#else
+ template<typename BasicJsonType>
+ static std::string get_byte_positions(const BasicJsonType* leaf_element)
+ {
+ static_cast<void>(leaf_element);
+ return "";
+ }
+#endif
+};
+
+/// @brief exception indicating a parse error
+/// @sa https://json.nlohmann.me/api/basic_json/parse_error/
+class parse_error : public exception
+{
+ public:
+ /*!
+ @brief create a parse error exception
+ @param[in] id_ the id of the exception
+ @param[in] pos the position where the error occurred (or with
+ chars_read_total=0 if the position cannot be
+ determined)
+ @param[in] what_arg the explanatory string
+ @return parse_error object
+ */
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("parse_error", id_), "parse error",
+ position_string(pos), ": ", exception::diagnostics(context), what_arg);
+ return {id_, pos.chars_read_total, w.c_str()};
+ }
+
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("parse_error", id_), "parse error",
+ (byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""),
+ ": ", exception::diagnostics(context), what_arg);
+ return {id_, byte_, w.c_str()};
+ }
+
+ /*!
+ @brief byte index of the parse error
+
+ The byte index of the last read character in the input file.
+
+ @note For an input with n bytes, 1 is the index of the first character and
+ n+1 is the index of the terminating null byte or the end of file.
+ This also holds true when reading a byte vector (CBOR or MessagePack).
+ */
+ const std::size_t byte;
+
+ private:
+ parse_error(int id_, std::size_t byte_, const char* what_arg)
+ : exception(id_, what_arg), byte(byte_) {}
+
+ static std::string position_string(const position_t& pos)
+ {
+ return concat(" at line ", std::to_string(pos.lines_read + 1),
+ ", column ", std::to_string(pos.chars_read_current_line));
+ }
+};
+
+/// @brief exception indicating errors with iterators
+/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/
+class invalid_iterator : public exception
+{
+ public:
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("invalid_iterator", id_), exception::diagnostics(context), what_arg);
+ return {id_, w.c_str()};
+ }
+
+ private:
+ JSON_HEDLEY_NON_NULL(3)
+ invalid_iterator(int id_, const char* what_arg)
+ : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating executing a member function with a wrong type
+/// @sa https://json.nlohmann.me/api/basic_json/type_error/
+class type_error : public exception
+{
+ public:
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("type_error", id_), exception::diagnostics(context), what_arg);
+ return {id_, w.c_str()};
+ }
+
+ private:
+ JSON_HEDLEY_NON_NULL(3)
+ type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating access out of the defined range
+/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/
+class out_of_range : public exception
+{
+ public:
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("out_of_range", id_), exception::diagnostics(context), what_arg);
+ return {id_, w.c_str()};
+ }
+
+ private:
+ JSON_HEDLEY_NON_NULL(3)
+ out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+/// @brief exception indicating other library errors
+/// @sa https://json.nlohmann.me/api/basic_json/other_error/
+class other_error : public exception
+{
+ public:
+ template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
+ static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
+ {
+ const std::string w = concat(exception::name("other_error", id_), exception::diagnostics(context), what_arg);
+ return {id_, w.c_str()};
+ }
+
+ private:
+ JSON_HEDLEY_NON_NULL(3)
+ other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+#if defined(__clang__)
+ #pragma clang diagnostic pop
+#endif
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// dispatching helper struct
+template <class T> struct identity_tag {};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/std_fs.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+#if JSON_HAS_EXPERIMENTAL_FILESYSTEM
+#include <experimental/filesystem>
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::experimental::filesystem;
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+#elif JSON_HAS_FILESYSTEM
+#include <filesystem> // NOLINT(build/c++17)
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+namespace std_fs = std::filesystem;
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+#endif
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
+ }
+ n = nullptr;
+}
+
+#ifdef JSON_HAS_CPP_17
+#ifndef JSON_USE_IMPLICIT_CONVERSIONS
+template<typename BasicJsonType, typename T>
+void from_json(const BasicJsonType& j, std::optional<T>& opt)
+{
+ if (j.is_null())
+ {
+ opt = std::nullopt;
+ }
+ else
+ {
+ opt.emplace(j.template get<T>());
+ }
+}
+
+#endif // JSON_USE_IMPLICIT_CONVERSIONS
+#endif // JSON_HAS_CPP_17
+
+// overloads for basic_json template parameters
+template < typename BasicJsonType, typename ArithmeticType,
+ enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int > = 0 >
+void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
+ {
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+
+ case value_t::null:
+ case value_t::object:
+ case value_t::array:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+ }
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
+ }
+ b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+ }
+ s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template <
+ typename BasicJsonType, typename StringType,
+ enable_if_t <
+ std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
+ && is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value
+ && !std::is_same<typename BasicJsonType::string_t, StringType>::value
+ && !is_json_ref<StringType>::value, int > = 0 >
+inline void from_json(const BasicJsonType& j, StringType& s)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+ }
+
+ s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
+{
+ get_arithmetic_value(j, val);
+}
+
+#if !JSON_DISABLE_ENUM_SERIALIZATION
+template<typename BasicJsonType, typename EnumType,
+ enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, EnumType& e)
+{
+ typename std::underlying_type<EnumType>::type val;
+ get_arithmetic_value(j, val);
+ e = static_cast<EnumType>(val);
+}
+#endif // JSON_DISABLE_ENUM_SERIALIZATION
+
+// forward_list doesn't have an insert method
+template<typename BasicJsonType, typename T, typename Allocator,
+ enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+ l.clear();
+ std::transform(j.rbegin(), j.rend(),
+ std::front_inserter(l), [](const BasicJsonType & i)
+ {
+ return i.template get<T>();
+ });
+}
+
+// valarray doesn't have an insert method
+template<typename BasicJsonType, typename T,
+ enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+ l.resize(j.size());
+ std::transform(j.begin(), j.end(), std::begin(l),
+ [](const BasicJsonType & elem)
+ {
+ return elem.template get<T>();
+ });
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+-> decltype(j.template get<T>(), void())
+{
+ for (std::size_t i = 0; i < N; ++i)
+ {
+ arr[i] = j.at(i).template get<T>();
+ }
+}
+
+template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2>
+auto from_json(const BasicJsonType& j, T (&arr)[N1][N2]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+-> decltype(j.template get<T>(), void())
+{
+ for (std::size_t i1 = 0; i1 < N1; ++i1)
+ {
+ for (std::size_t i2 = 0; i2 < N2; ++i2)
+ {
+ arr[i1][i2] = j.at(i1).at(i2).template get<T>();
+ }
+ }
+}
+
+template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3>
+auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+-> decltype(j.template get<T>(), void())
+{
+ for (std::size_t i1 = 0; i1 < N1; ++i1)
+ {
+ for (std::size_t i2 = 0; i2 < N2; ++i2)
+ {
+ for (std::size_t i3 = 0; i3 < N3; ++i3)
+ {
+ arr[i1][i2][i3] = j.at(i1).at(i2).at(i3).template get<T>();
+ }
+ }
+ }
+}
+
+template<typename BasicJsonType, typename T, std::size_t N1, std::size_t N2, std::size_t N3, std::size_t N4>
+auto from_json(const BasicJsonType& j, T (&arr)[N1][N2][N3][N4]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+-> decltype(j.template get<T>(), void())
+{
+ for (std::size_t i1 = 0; i1 < N1; ++i1)
+ {
+ for (std::size_t i2 = 0; i2 < N2; ++i2)
+ {
+ for (std::size_t i3 = 0; i3 < N3; ++i3)
+ {
+ for (std::size_t i4 = 0; i4 < N4; ++i4)
+ {
+ arr[i1][i2][i3][i4] = j.at(i1).at(i2).at(i3).at(i4).template get<T>();
+ }
+ }
+ }
+ }
+}
+
+template<typename BasicJsonType>
+inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
+{
+ arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
+}
+
+template<typename BasicJsonType, typename T, std::size_t N>
+auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
+ priority_tag<2> /*unused*/)
+-> decltype(j.template get<T>(), void())
+{
+ for (std::size_t i = 0; i < N; ++i)
+ {
+ arr[i] = j.at(i).template get<T>();
+ }
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+ enable_if_t<
+ std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+ int> = 0>
+auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
+-> decltype(
+ arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),
+ j.template get<typename ConstructibleArrayType::value_type>(),
+ void())
+{
+ using std::end;
+
+ ConstructibleArrayType ret;
+ ret.reserve(j.size());
+ std::transform(j.begin(), j.end(),
+ std::inserter(ret, end(ret)), [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename ConstructibleArrayType::value_type>();
+ });
+ arr = std::move(ret);
+}
+
+template<typename BasicJsonType, typename ConstructibleArrayType,
+ enable_if_t<
+ std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
+ int> = 0>
+inline void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,
+ priority_tag<0> /*unused*/)
+{
+ using std::end;
+
+ ConstructibleArrayType ret;
+ std::transform(
+ j.begin(), j.end(), std::inserter(ret, end(ret)),
+ [](const BasicJsonType & i)
+ {
+ // get<BasicJsonType>() returns *this, this won't call a from_json
+ // method when value_type is BasicJsonType
+ return i.template get<typename ConstructibleArrayType::value_type>();
+ });
+ arr = std::move(ret);
+}
+
+template < typename BasicJsonType, typename ConstructibleArrayType,
+ enable_if_t <
+ is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
+ !is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
+ !is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
+ !std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
+ !is_basic_json<ConstructibleArrayType>::value,
+ int > = 0 >
+auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
+-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
+j.template get<typename ConstructibleArrayType::value_type>(),
+void())
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+
+ from_json_array_impl(j, arr, priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename T, std::size_t... Idx >
+std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(BasicJsonType&& j,
+ identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
+{
+ return { { std::forward<BasicJsonType>(j).at(Idx).template get<T>()... } };
+}
+
+template < typename BasicJsonType, typename T, std::size_t N >
+auto from_json(BasicJsonType&& j, identity_tag<std::array<T, N>> tag)
+-> decltype(from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {}))
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+
+ return from_json_inplace_array_impl(std::forward<BasicJsonType>(j), tag, make_index_sequence<N> {});
+}
+
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
+ }
+
+ bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
+}
+
+template<typename BasicJsonType, typename ConstructibleObjectType,
+ enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
+inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
+ }
+
+ ConstructibleObjectType ret;
+ const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
+ using value_type = typename ConstructibleObjectType::value_type;
+ std::transform(
+ inner_object->begin(), inner_object->end(),
+ std::inserter(ret, ret.begin()),
+ [](typename BasicJsonType::object_t::value_type const & p)
+ {
+ return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
+ });
+ obj = std::move(ret);
+}
+
+// overload for arithmetic types, not chosen for basic_json template arguments
+// (BooleanType, etc..); note: Is it really necessary to provide explicit
+// overloads for boolean_t etc. in case of a custom BooleanType which is not
+// an arithmetic type?
+template < typename BasicJsonType, typename ArithmeticType,
+ enable_if_t <
+ std::is_arithmetic<ArithmeticType>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
+ !std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
+ int > = 0 >
+inline void from_json(const BasicJsonType& j, ArithmeticType& val)
+{
+ switch (static_cast<value_t>(j))
+ {
+ case value_t::number_unsigned:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
+ break;
+ }
+ case value_t::number_integer:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
+ break;
+ }
+ case value_t::number_float:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
+ break;
+ }
+ case value_t::boolean:
+ {
+ val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
+ break;
+ }
+
+ case value_t::null:
+ case value_t::object:
+ case value_t::array:
+ case value_t::string:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
+ }
+}
+
+template<typename BasicJsonType, typename... Args, std::size_t... Idx>
+std::tuple<Args...> from_json_tuple_impl_base(BasicJsonType&& j, index_sequence<Idx...> /*unused*/)
+{
+ return std::make_tuple(std::forward<BasicJsonType>(j).at(Idx).template get<Args>()...);
+}
+
+template<typename BasicJsonType>
+std::tuple<> from_json_tuple_impl_base(BasicJsonType& /*unused*/, index_sequence<> /*unused*/)
+{
+ return {};
+}
+
+template < typename BasicJsonType, class A1, class A2 >
+std::pair<A1, A2> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
+{
+ return {std::forward<BasicJsonType>(j).at(0).template get<A1>(),
+ std::forward<BasicJsonType>(j).at(1).template get<A2>()};
+}
+
+template<typename BasicJsonType, typename A1, typename A2>
+inline void from_json_tuple_impl(BasicJsonType&& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
+{
+ p = from_json_tuple_impl(std::forward<BasicJsonType>(j), identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+std::tuple<Args...> from_json_tuple_impl(BasicJsonType&& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
+{
+ return from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
+}
+
+template<typename BasicJsonType, typename... Args>
+inline void from_json_tuple_impl(BasicJsonType&& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
+{
+ t = from_json_tuple_impl_base<BasicJsonType, Args...>(std::forward<BasicJsonType>(j), index_sequence_for<Args...> {});
+}
+
+template<typename BasicJsonType, typename TupleRelated>
+auto from_json(BasicJsonType&& j, TupleRelated&& t)
+-> decltype(from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {}))
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+
+ return from_json_tuple_impl(std::forward<BasicJsonType>(j), std::forward<TupleRelated>(t), priority_tag<3> {});
+}
+
+template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
+ typename = enable_if_t < !std::is_constructible <
+ typename BasicJsonType::string_t, Key >::value >>
+inline void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+ m.clear();
+ for (const auto& p : j)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+ }
+ m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+ }
+}
+
+template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
+ typename = enable_if_t < !std::is_constructible <
+ typename BasicJsonType::string_t, Key >::value >>
+inline void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
+ }
+ m.clear();
+ for (const auto& p : j)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &j));
+ }
+ m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
+ }
+}
+
+#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+template<typename BasicJsonType>
+inline void from_json(const BasicJsonType& j, std_fs::path& p)
+{
+ if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
+ {
+ JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
+ }
+ const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
+#ifdef JSON_HAS_CPP_20
+ p = std_fs::path(std::u8string_view(reinterpret_cast<const char8_t*>(s.data()), s.size()));
+#else
+ p = std_fs::u8path(s); // accepts UTF-8 encoded std::string in C++17, deprecated in C++20
+#endif
+}
+#endif
+
+struct from_json_fn
+{
+ template<typename BasicJsonType, typename T>
+ auto operator()(const BasicJsonType& j, T&& val) const
+ noexcept(noexcept(from_json(j, std::forward<T>(val))))
+ -> decltype(from_json(j, std::forward<T>(val)))
+ {
+ return from_json(j, std::forward<T>(val));
+ }
+};
+
+} // namespace detail
+
+#ifndef JSON_HAS_CPP_17
+/// namespace to hold default `from_json` function
+/// to see why this is required:
+/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
+ detail::static_const<detail::from_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
+} // namespace
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/macro_scope.hpp>
+// JSON_HAS_CPP_17
+#ifdef JSON_HAS_CPP_17
+ #include <optional> // optional
+#endif
+
+#include <algorithm> // copy
+#include <iterator> // begin, end
+#include <string> // string
+#include <tuple> // tuple, get
+#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
+#include <utility> // move, forward, declval, pair
+#include <valarray> // valarray
+#include <vector> // vector
+
+// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // size_t
+#include <iterator> // forward_iterator_tag
+#include <tuple> // tuple_size, get, tuple_element
+#include <utility> // move
+
+#if JSON_HAS_RANGES
+ #include <ranges> // enable_borrowed_range
+#endif
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/string_utils.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // size_t
+#include <string> // string, to_string
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename StringType>
+void int_to_string(StringType& target, std::size_t value)
+{
+ // For ADL
+ using std::to_string;
+ target = to_string(value);
+}
+
+template<typename StringType>
+StringType to_string(std::size_t value)
+{
+ StringType result;
+ int_to_string(result, value);
+ return result;
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename IteratorType> class iteration_proxy_value
+{
+ public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = iteration_proxy_value;
+ using pointer = value_type *;
+ using reference = value_type &;
+ using iterator_category = std::forward_iterator_tag;
+ using string_type = typename std::remove_cv< typename std::remove_reference<decltype( std::declval<IteratorType>().key() ) >::type >::type;
+
+ private:
+ /// the iterator
+ IteratorType anchor{};
+ /// an index for arrays (used to create key names)
+ std::size_t array_index = 0;
+ /// last stringified array index
+ mutable std::size_t array_index_last = 0;
+ /// a string representation of the array index
+ mutable string_type array_index_str = "0";
+ /// an empty string (to return a reference for primitive values)
+ string_type empty_str{};
+
+ public:
+ explicit iteration_proxy_value() = default;
+ explicit iteration_proxy_value(IteratorType it, std::size_t array_index_ = 0)
+ noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+ && std::is_nothrow_default_constructible<string_type>::value)
+ : anchor(std::move(it))
+ , array_index(array_index_)
+ {}
+
+ iteration_proxy_value(iteration_proxy_value const&) = default;
+ iteration_proxy_value& operator=(iteration_proxy_value const&) = default;
+ // older GCCs are a bit fussy and require explicit noexcept specifiers on defaulted functions
+ iteration_proxy_value(iteration_proxy_value&&)
+ noexcept(std::is_nothrow_move_constructible<IteratorType>::value
+ && std::is_nothrow_move_constructible<string_type>::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations)
+ iteration_proxy_value& operator=(iteration_proxy_value&&)
+ noexcept(std::is_nothrow_move_assignable<IteratorType>::value
+ && std::is_nothrow_move_assignable<string_type>::value) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor,cppcoreguidelines-noexcept-move-operations)
+ ~iteration_proxy_value() = default;
+
+ /// dereference operator (needed for range-based for)
+ const iteration_proxy_value& operator*() const
+ {
+ return *this;
+ }
+
+ /// increment operator (needed for range-based for)
+ iteration_proxy_value& operator++()
+ {
+ ++anchor;
+ ++array_index;
+
+ return *this;
+ }
+
+ iteration_proxy_value operator++(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ auto tmp = iteration_proxy_value(anchor, array_index);
+ ++anchor;
+ ++array_index;
+ return tmp;
+ }
+
+ /// equality operator (needed for InputIterator)
+ bool operator==(const iteration_proxy_value& o) const
+ {
+ return anchor == o.anchor;
+ }
+
+ /// inequality operator (needed for range-based for)
+ bool operator!=(const iteration_proxy_value& o) const
+ {
+ return anchor != o.anchor;
+ }
+
+ /// return key of the iterator
+ const string_type& key() const
+ {
+ JSON_ASSERT(anchor.m_object != nullptr);
+
+ switch (anchor.m_object->type())
+ {
+ // use integer array index as key
+ case value_t::array:
+ {
+ if (array_index != array_index_last)
+ {
+ int_to_string( array_index_str, array_index );
+ array_index_last = array_index;
+ }
+ return array_index_str;
+ }
+
+ // use key from the object
+ case value_t::object:
+ return anchor.key();
+
+ // use an empty key for all primitive types
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ return empty_str;
+ }
+ }
+
+ /// return value of the iterator
+ typename IteratorType::reference value() const
+ {
+ return anchor.value();
+ }
+};
+
+/// proxy class for the items() function
+template<typename IteratorType> class iteration_proxy
+{
+ private:
+ /// the container to iterate
+ typename IteratorType::pointer container = nullptr;
+
+ public:
+ explicit iteration_proxy() = default;
+
+ /// construct iteration proxy from a container
+ explicit iteration_proxy(typename IteratorType::reference cont) noexcept
+ : container(&cont) {}
+
+ iteration_proxy(iteration_proxy const&) = default;
+ iteration_proxy& operator=(iteration_proxy const&) = default;
+ iteration_proxy(iteration_proxy&&) noexcept = default;
+ iteration_proxy& operator=(iteration_proxy&&) noexcept = default;
+ ~iteration_proxy() = default;
+
+ /// return iterator begin (needed for range-based for)
+ iteration_proxy_value<IteratorType> begin() const noexcept
+ {
+ return iteration_proxy_value<IteratorType>(container->begin());
+ }
+
+ /// return iterator end (needed for range-based for)
+ iteration_proxy_value<IteratorType> end() const noexcept
+ {
+ return iteration_proxy_value<IteratorType>(container->end());
+ }
+};
+
+// Structured Bindings Support
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+template<std::size_t N, typename IteratorType, enable_if_t<N == 0, int> = 0>
+auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.key())
+{
+ return i.key();
+}
+// Structured Bindings Support
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+template<std::size_t N, typename IteratorType, enable_if_t<N == 1, int> = 0>
+auto get(const nlohmann::detail::iteration_proxy_value<IteratorType>& i) -> decltype(i.value())
+{
+ return i.value();
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// The Addition to the STD Namespace is required to add
+// Structured Bindings Support to the iteration_proxy_value class
+// For further reference see https://blog.tartanllama.xyz/structured-bindings/
+// And see https://github.com/nlohmann/json/pull/1391
+namespace std
+{
+
+#if defined(__clang__)
+ // Fix: https://github.com/nlohmann/json/issues/1401
+ #pragma clang diagnostic push
+ #pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+template<typename IteratorType>
+class tuple_size<::nlohmann::detail::iteration_proxy_value<IteratorType>> // NOLINT(cert-dcl58-cpp)
+ : public std::integral_constant<std::size_t, 2> {};
+
+template<std::size_t N, typename IteratorType>
+class tuple_element<N, ::nlohmann::detail::iteration_proxy_value<IteratorType >> // NOLINT(cert-dcl58-cpp)
+{
+ public:
+ using type = decltype(
+ get<N>(std::declval <
+ ::nlohmann::detail::iteration_proxy_value<IteratorType >> ()));
+};
+#if defined(__clang__)
+ #pragma clang diagnostic pop
+#endif
+
+} // namespace std
+
+#if JSON_HAS_RANGES
+ template <typename IteratorType>
+ inline constexpr bool ::std::ranges::enable_borrowed_range<::nlohmann::detail::iteration_proxy<IteratorType>> = true;
+#endif
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/std_fs.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////
+// constructors //
+//////////////////
+
+/*
+ * Note all external_constructor<>::construct functions need to call
+ * j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
+ * allocated value (e.g., a string). See bug issue
+ * https://github.com/nlohmann/json/issues/2865 for more information.
+ */
+
+template<value_t> struct external_constructor;
+
+template<>
+struct external_constructor<value_t::boolean>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::boolean;
+ j.m_data.m_value = b;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::string>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::string;
+ j.m_data.m_value = s;
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::string;
+ j.m_data.m_value = std::move(s);
+ j.assert_invariant();
+ }
+
+ template < typename BasicJsonType, typename CompatibleStringType,
+ enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
+ int > = 0 >
+ static void construct(BasicJsonType& j, const CompatibleStringType& str)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::string;
+ j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::binary>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::binary;
+ j.m_data.m_value = typename BasicJsonType::binary_t(b);
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::binary;
+ j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_float>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::number_float;
+ j.m_data.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_unsigned>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::number_unsigned;
+ j.m_data.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::number_integer>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::number_integer;
+ j.m_data.m_value = val;
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::array>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::array;
+ j.m_data.m_value = arr;
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::array;
+ j.m_data.m_value = std::move(arr);
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template < typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,
+ int > = 0 >
+ static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
+ {
+ using std::begin;
+ using std::end;
+
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::array;
+ j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const std::vector<bool>& arr)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::array;
+ j.m_data.m_value = value_t::array;
+ j.m_data.m_value.array->reserve(arr.size());
+ for (const bool x : arr)
+ {
+ j.m_data.m_value.array->push_back(x);
+ j.set_parent(j.m_data.m_value.array->back());
+ }
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+ static void construct(BasicJsonType& j, const std::valarray<T>& arr)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::array;
+ j.m_data.m_value = value_t::array;
+ j.m_data.m_value.array->resize(arr.size());
+ if (arr.size() > 0)
+ {
+ std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
+ }
+ j.set_parents();
+ j.assert_invariant();
+ }
+};
+
+template<>
+struct external_constructor<value_t::object>
+{
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::object;
+ j.m_data.m_value = obj;
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template<typename BasicJsonType>
+ static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+ {
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::object;
+ j.m_data.m_value = std::move(obj);
+ j.set_parents();
+ j.assert_invariant();
+ }
+
+ template < typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
+ static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
+ {
+ using std::begin;
+ using std::end;
+
+ j.m_data.m_value.destroy(j.m_data.m_type);
+ j.m_data.m_type = value_t::object;
+ j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
+ j.set_parents();
+ j.assert_invariant();
+ }
+};
+
+/////////////
+// to_json //
+/////////////
+
+#ifdef JSON_HAS_CPP_17
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
+void to_json(BasicJsonType& j, const std::optional<T>& opt)
+{
+ if (opt.has_value())
+ {
+ j = *opt;
+ }
+ else
+ {
+ j = nullptr;
+ }
+}
+#endif
+
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
+inline void to_json(BasicJsonType& j, T b) noexcept
+{
+ external_constructor<value_t::boolean>::construct(j, b);
+}
+
+template < typename BasicJsonType, typename BoolRef,
+ enable_if_t <
+ ((std::is_same<std::vector<bool>::reference, BoolRef>::value
+ && !std::is_same <std::vector<bool>::reference, typename BasicJsonType::boolean_t&>::value)
+ || (std::is_same<std::vector<bool>::const_reference, BoolRef>::value
+ && !std::is_same <detail::uncvref_t<std::vector<bool>::const_reference>,
+ typename BasicJsonType::boolean_t >::value))
+ && std::is_convertible<const BoolRef&, typename BasicJsonType::boolean_t>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const BoolRef& b) noexcept
+{
+ external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));
+}
+
+template<typename BasicJsonType, typename CompatibleString,
+ enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const CompatibleString& s)
+{
+ external_constructor<value_t::string>::construct(j, s);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
+{
+ external_constructor<value_t::string>::construct(j, std::move(s));
+}
+
+template<typename BasicJsonType, typename FloatType,
+ enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, FloatType val) noexcept
+{
+ external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
+ enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
+{
+ external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
+}
+
+template<typename BasicJsonType, typename CompatibleNumberIntegerType,
+ enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
+{
+ external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
+}
+
+#if !JSON_DISABLE_ENUM_SERIALIZATION
+template<typename BasicJsonType, typename EnumType,
+ enable_if_t<std::is_enum<EnumType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, EnumType e) noexcept
+{
+ using underlying_type = typename std::underlying_type<EnumType>::type;
+ static constexpr value_t integral_value_t = std::is_unsigned<underlying_type>::value ? value_t::number_unsigned : value_t::number_integer;
+ external_constructor<integral_value_t>::construct(j, static_cast<underlying_type>(e));
+}
+#endif // JSON_DISABLE_ENUM_SERIALIZATION
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
+{
+ external_constructor<value_t::array>::construct(j, e);
+}
+
+template < typename BasicJsonType, typename CompatibleArrayType,
+ enable_if_t < is_compatible_array_type<BasicJsonType,
+ CompatibleArrayType>::value&&
+ !is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
+ !is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
+ !std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
+ !is_basic_json<CompatibleArrayType>::value,
+ int > = 0 >
+inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
+{
+ external_constructor<value_t::array>::construct(j, arr);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
+{
+ external_constructor<value_t::binary>::construct(j, bin);
+}
+
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
+{
+ external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
+{
+ external_constructor<value_t::array>::construct(j, std::move(arr));
+}
+
+template < typename BasicJsonType, typename CompatibleObjectType,
+ enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
+{
+ external_constructor<value_t::object>::construct(j, obj);
+}
+
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
+{
+ external_constructor<value_t::object>::construct(j, std::move(obj));
+}
+
+template <
+ typename BasicJsonType, typename T, std::size_t N,
+ enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
+ const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ int > = 0 >
+inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+{
+ external_constructor<value_t::array>::construct(j, arr);
+}
+
+template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
+inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
+{
+ j = { p.first, p.second };
+}
+
+// for https://github.com/nlohmann/json/pull/1134
+template<typename BasicJsonType, typename T,
+ enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
+inline void to_json(BasicJsonType& j, const T& b)
+{
+ j = { {b.key(), b.value()} };
+}
+
+template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
+inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
+{
+ j = { std::get<Idx>(t)... };
+}
+
+template<typename BasicJsonType, typename Tuple>
+inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
+{
+ using array_t = typename BasicJsonType::array_t;
+ j = array_t();
+}
+
+template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
+inline void to_json(BasicJsonType& j, const T& t)
+{
+ to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
+}
+
+#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
+template<typename BasicJsonType>
+inline void to_json(BasicJsonType& j, const std_fs::path& p)
+{
+#ifdef JSON_HAS_CPP_20
+ const std::u8string s = p.u8string();
+ j = std::string(s.begin(), s.end());
+#else
+ j = p.u8string(); // returns std::string in C++17
+#endif
+}
+#endif
+
+struct to_json_fn
+{
+ template<typename BasicJsonType, typename T>
+ auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
+ -> decltype(to_json(j, std::forward<T>(val)), void())
+ {
+ return to_json(j, std::forward<T>(val));
+ }
+};
+} // namespace detail
+
+#ifndef JSON_HAS_CPP_17
+/// namespace to hold default `to_json` function
+/// to see why this is required:
+/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
+namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces)
+{
+#endif
+JSON_INLINE_VARIABLE constexpr const auto& to_json = // NOLINT(misc-definitions-in-headers)
+ detail::static_const<detail::to_json_fn>::value;
+#ifndef JSON_HAS_CPP_17
+} // namespace
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/identity_tag.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @sa https://json.nlohmann.me/api/adl_serializer/
+template<typename ValueType, typename>
+struct adl_serializer
+{
+ /// @brief convert a JSON value to any value type
+ /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+ template<typename BasicJsonType, typename TargetType = ValueType>
+ static auto from_json(BasicJsonType && j, TargetType& val) noexcept(
+ noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))
+ -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())
+ {
+ ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);
+ }
+
+ /// @brief convert a JSON value to any value type
+ /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/
+ template<typename BasicJsonType, typename TargetType = ValueType>
+ static auto from_json(BasicJsonType && j) noexcept(
+ noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {})))
+ -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {}))
+ {
+ return ::nlohmann::from_json(std::forward<BasicJsonType>(j), detail::identity_tag<TargetType> {});
+ }
+
+ /// @brief convert any value type to a JSON value
+ /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/
+ template<typename BasicJsonType, typename TargetType = ValueType>
+ static auto to_json(BasicJsonType& j, TargetType && val) noexcept(
+ noexcept(::nlohmann::to_json(j, std::forward<TargetType>(val))))
+ -> decltype(::nlohmann::to_json(j, std::forward<TargetType>(val)), void())
+ {
+ ::nlohmann::to_json(j, std::forward<TargetType>(val));
+ }
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/byte_container_with_subtype.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // uint8_t, uint64_t
+#include <tuple> // tie
+#include <utility> // move
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @brief an internal type for a backed binary type
+/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/
+template<typename BinaryType>
+class byte_container_with_subtype : public BinaryType
+{
+ public:
+ using container_type = BinaryType;
+ using subtype_type = std::uint64_t;
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype() noexcept(noexcept(container_type()))
+ : container_type()
+ {}
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b)))
+ : container_type(b)
+ {}
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b))))
+ : container_type(std::move(b))
+ {}
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b)))
+ : container_type(b)
+ , m_subtype(subtype_)
+ , m_has_subtype(true)
+ {}
+
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/
+ byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b))))
+ : container_type(std::move(b))
+ , m_subtype(subtype_)
+ , m_has_subtype(true)
+ {}
+
+ bool operator==(const byte_container_with_subtype& rhs) const
+ {
+ return std::tie(static_cast<const BinaryType&>(*this), m_subtype, m_has_subtype) ==
+ std::tie(static_cast<const BinaryType&>(rhs), rhs.m_subtype, rhs.m_has_subtype);
+ }
+
+ bool operator!=(const byte_container_with_subtype& rhs) const
+ {
+ return !(rhs == *this);
+ }
+
+ /// @brief sets the binary subtype
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/
+ void set_subtype(subtype_type subtype_) noexcept
+ {
+ m_subtype = subtype_;
+ m_has_subtype = true;
+ }
+
+ /// @brief return the binary subtype
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/
+ constexpr subtype_type subtype() const noexcept
+ {
+ return m_has_subtype ? m_subtype : static_cast<subtype_type>(-1);
+ }
+
+ /// @brief return whether the value has a subtype
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/
+ constexpr bool has_subtype() const noexcept
+ {
+ return m_has_subtype;
+ }
+
+ /// @brief clears the binary subtype
+ /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/
+ void clear_subtype() noexcept
+ {
+ m_subtype = 0;
+ m_has_subtype = false;
+ }
+
+ private:
+ subtype_type m_subtype = 0;
+ bool m_has_subtype = false;
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/conversions/from_json.hpp>
+
+// #include <nlohmann/detail/conversions/to_json.hpp>
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/hash.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // uint8_t
+#include <cstddef> // size_t
+#include <functional> // hash
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// boost::hash_combine
+inline std::size_t combine(std::size_t seed, std::size_t h) noexcept
+{
+ seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U);
+ return seed;
+}
+
+/*!
+@brief hash a JSON value
+
+The hash function tries to rely on std::hash where possible. Furthermore, the
+type of the JSON value is taken into account to have different hash values for
+null, 0, 0U, and false, etc.
+
+@tparam BasicJsonType basic_json specialization
+@param j JSON value to hash
+@return hash value of j
+*/
+template<typename BasicJsonType>
+std::size_t hash(const BasicJsonType& j)
+{
+ using string_t = typename BasicJsonType::string_t;
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+
+ const auto type = static_cast<std::size_t>(j.type());
+ switch (j.type())
+ {
+ case BasicJsonType::value_t::null:
+ case BasicJsonType::value_t::discarded:
+ {
+ return combine(type, 0);
+ }
+
+ case BasicJsonType::value_t::object:
+ {
+ auto seed = combine(type, j.size());
+ for (const auto& element : j.items())
+ {
+ const auto h = std::hash<string_t> {}(element.key());
+ seed = combine(seed, h);
+ seed = combine(seed, hash(element.value()));
+ }
+ return seed;
+ }
+
+ case BasicJsonType::value_t::array:
+ {
+ auto seed = combine(type, j.size());
+ for (const auto& element : j)
+ {
+ seed = combine(seed, hash(element));
+ }
+ return seed;
+ }
+
+ case BasicJsonType::value_t::string:
+ {
+ const auto h = std::hash<string_t> {}(j.template get_ref<const string_t&>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::boolean:
+ {
+ const auto h = std::hash<bool> {}(j.template get<bool>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::number_integer:
+ {
+ const auto h = std::hash<number_integer_t> {}(j.template get<number_integer_t>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::number_unsigned:
+ {
+ const auto h = std::hash<number_unsigned_t> {}(j.template get<number_unsigned_t>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::number_float:
+ {
+ const auto h = std::hash<number_float_t> {}(j.template get<number_float_t>());
+ return combine(type, h);
+ }
+
+ case BasicJsonType::value_t::binary:
+ {
+ auto seed = combine(type, j.get_binary().size());
+ const auto h = std::hash<bool> {}(j.get_binary().has_subtype());
+ seed = combine(seed, h);
+ seed = combine(seed, static_cast<std::size_t>(j.get_binary().subtype()));
+ for (const auto byte : j.get_binary())
+ {
+ seed = combine(seed, std::hash<std::uint8_t> {}(byte));
+ }
+ return seed;
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ return 0; // LCOV_EXCL_LINE
+ }
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // generate_n
+#include <array> // array
+#include <cmath> // ldexp
+#include <cstddef> // size_t
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstdio> // snprintf
+#include <cstring> // memcpy
+#include <iterator> // back_inserter
+#include <limits> // numeric_limits
+#include <string> // char_traits, string
+#include <utility> // make_pair, move
+#include <vector> // vector
+#ifdef __cpp_lib_byteswap
+ #include <bit> //byteswap
+#endif
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cstddef> // size_t
+#include <cstring> // strlen
+#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
+#include <memory> // shared_ptr, make_shared, addressof
+#include <numeric> // accumulate
+#include <string> // string, char_traits
+#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
+#include <utility> // pair, declval
+
+#ifndef JSON_NO_IO
+ #include <cstdio> // FILE *
+ #include <istream> // istream
+#endif // JSON_NO_IO
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/iterators/iterator_traits.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// the supported input formats
+enum class input_format_t { json, cbor, msgpack, ubjson, bson, bjdata };
+
+////////////////////
+// input adapters //
+////////////////////
+
+#ifndef JSON_NO_IO
+/*!
+Input adapter for stdio file access. This adapter read only 1 byte and do not use any
+ buffer. This adapter is a very low level adapter.
+*/
+class file_input_adapter
+{
+ public:
+ using char_type = char;
+
+ JSON_HEDLEY_NON_NULL(2)
+ explicit file_input_adapter(std::FILE* f) noexcept
+ : m_file(f)
+ {
+ JSON_ASSERT(m_file != nullptr);
+ }
+
+ // make class move-only
+ file_input_adapter(const file_input_adapter&) = delete;
+ file_input_adapter(file_input_adapter&&) noexcept = default;
+ file_input_adapter& operator=(const file_input_adapter&) = delete;
+ file_input_adapter& operator=(file_input_adapter&&) = delete;
+ ~file_input_adapter() = default;
+
+ std::char_traits<char>::int_type get_character() noexcept
+ {
+ return std::fgetc(m_file);
+ }
+
+ // returns the number of characters successfully read
+ template<class T>
+ std::size_t get_elements(T* dest, std::size_t count = 1)
+ {
+ return fread(dest, 1, sizeof(T) * count, m_file);
+ }
+
+ private:
+ /// the file pointer to read from
+ std::FILE* m_file;
+};
+
+/*!
+Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at
+beginning of input. Does not support changing the underlying std::streambuf
+in mid-input. Maintains underlying std::istream and std::streambuf to support
+subsequent use of standard std::istream operations to process any input
+characters following those used in parsing the JSON input. Clears the
+std::istream flags; any input errors (e.g., EOF) will be detected by the first
+subsequent call for input from the std::istream.
+*/
+class input_stream_adapter
+{
+ public:
+ using char_type = char;
+
+ ~input_stream_adapter()
+ {
+ // clear stream flags; we use underlying streambuf I/O, do not
+ // maintain ifstream flags, except eof
+ if (is != nullptr)
+ {
+ is->clear(is->rdstate() & std::ios::eofbit);
+ }
+ }
+
+ explicit input_stream_adapter(std::istream& i)
+ : is(&i), sb(i.rdbuf())
+ {}
+
+ // delete because of pointer members
+ input_stream_adapter(const input_stream_adapter&) = delete;
+ input_stream_adapter& operator=(input_stream_adapter&) = delete;
+ input_stream_adapter& operator=(input_stream_adapter&&) = delete;
+
+ input_stream_adapter(input_stream_adapter&& rhs) noexcept
+ : is(rhs.is), sb(rhs.sb)
+ {
+ rhs.is = nullptr;
+ rhs.sb = nullptr;
+ }
+
+ // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
+ // ensure that std::char_traits<char>::eof() and the character 0xFF do not
+ // end up as the same value, e.g. 0xFFFFFFFF.
+ std::char_traits<char>::int_type get_character()
+ {
+ auto res = sb->sbumpc();
+ // set eof manually, as we don't use the istream interface.
+ if (JSON_HEDLEY_UNLIKELY(res == std::char_traits<char>::eof()))
+ {
+ is->clear(is->rdstate() | std::ios::eofbit);
+ }
+ return res;
+ }
+
+ template<class T>
+ std::size_t get_elements(T* dest, std::size_t count = 1)
+ {
+ auto res = static_cast<std::size_t>(sb->sgetn(reinterpret_cast<char*>(dest), static_cast<std::streamsize>(count * sizeof(T))));
+ if (JSON_HEDLEY_UNLIKELY(res < count * sizeof(T)))
+ {
+ is->clear(is->rdstate() | std::ios::eofbit);
+ }
+ return res;
+ }
+
+ private:
+ /// the associated input stream
+ std::istream* is = nullptr;
+ std::streambuf* sb = nullptr;
+};
+#endif // JSON_NO_IO
+
+// General-purpose iterator-based adapter. It might not be as fast as
+// theoretically possible for some containers, but it is extremely versatile.
+template<typename IteratorType>
+class iterator_input_adapter
+{
+ public:
+ using char_type = typename std::iterator_traits<IteratorType>::value_type;
+
+ iterator_input_adapter(IteratorType first, IteratorType last)
+ : current(std::move(first)), end(std::move(last))
+ {}
+
+ typename char_traits<char_type>::int_type get_character()
+ {
+ if (JSON_HEDLEY_LIKELY(current != end))
+ {
+ auto result = char_traits<char_type>::to_int_type(*current);
+ std::advance(current, 1);
+ return result;
+ }
+
+ return char_traits<char_type>::eof();
+ }
+
+ // for general iterators, we cannot really do something better than falling back to processing the range one-by-one
+ template<class T>
+ std::size_t get_elements(T* dest, std::size_t count = 1)
+ {
+ auto* ptr = reinterpret_cast<char*>(dest);
+ for (std::size_t read_index = 0; read_index < count * sizeof(T); ++read_index)
+ {
+ if (JSON_HEDLEY_LIKELY(current != end))
+ {
+ ptr[read_index] = static_cast<char>(*current);
+ std::advance(current, 1);
+ }
+ else
+ {
+ return read_index;
+ }
+ }
+ return count * sizeof(T);
+ }
+
+ private:
+ IteratorType current;
+ IteratorType end;
+
+ template<typename BaseInputAdapter, size_t T>
+ friend struct wide_string_input_helper;
+
+ bool empty() const
+ {
+ return current == end;
+ }
+};
+
+template<typename BaseInputAdapter, size_t T>
+struct wide_string_input_helper;
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 4>
+{
+ // UTF-32
+ static void fill_buffer(BaseInputAdapter& input,
+ std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+ size_t& utf8_bytes_index,
+ size_t& utf8_bytes_filled)
+ {
+ utf8_bytes_index = 0;
+
+ if (JSON_HEDLEY_UNLIKELY(input.empty()))
+ {
+ utf8_bytes[0] = std::char_traits<char>::eof();
+ utf8_bytes_filled = 1;
+ }
+ else
+ {
+ // get the current character
+ const auto wc = input.get_character();
+
+ // UTF-32 to UTF-8 encoding
+ if (wc < 0x80)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+ utf8_bytes_filled = 1;
+ }
+ else if (wc <= 0x7FF)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u) & 0x1Fu));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 2;
+ }
+ else if (wc <= 0xFFFF)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u) & 0x0Fu));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+ utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 3;
+ }
+ else if (wc <= 0x10FFFF)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | ((static_cast<unsigned int>(wc) >> 18u) & 0x07u));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 12u) & 0x3Fu));
+ utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+ utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 4;
+ }
+ else
+ {
+ // unknown character
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+ utf8_bytes_filled = 1;
+ }
+ }
+ }
+};
+
+template<typename BaseInputAdapter>
+struct wide_string_input_helper<BaseInputAdapter, 2>
+{
+ // UTF-16
+ static void fill_buffer(BaseInputAdapter& input,
+ std::array<std::char_traits<char>::int_type, 4>& utf8_bytes,
+ size_t& utf8_bytes_index,
+ size_t& utf8_bytes_filled)
+ {
+ utf8_bytes_index = 0;
+
+ if (JSON_HEDLEY_UNLIKELY(input.empty()))
+ {
+ utf8_bytes[0] = std::char_traits<char>::eof();
+ utf8_bytes_filled = 1;
+ }
+ else
+ {
+ // get the current character
+ const auto wc = input.get_character();
+
+ // UTF-16 to UTF-8 encoding
+ if (wc < 0x80)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+ utf8_bytes_filled = 1;
+ }
+ else if (wc <= 0x7FF)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xC0u | ((static_cast<unsigned int>(wc) >> 6u)));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 2;
+ }
+ else if (0xD800 > wc || wc >= 0xE000)
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xE0u | ((static_cast<unsigned int>(wc) >> 12u)));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((static_cast<unsigned int>(wc) >> 6u) & 0x3Fu));
+ utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | (static_cast<unsigned int>(wc) & 0x3Fu));
+ utf8_bytes_filled = 3;
+ }
+ else
+ {
+ if (JSON_HEDLEY_UNLIKELY(!input.empty()))
+ {
+ const auto wc2 = static_cast<unsigned int>(input.get_character());
+ const auto charcode = 0x10000u + (((static_cast<unsigned int>(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu));
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(0xF0u | (charcode >> 18u));
+ utf8_bytes[1] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu));
+ utf8_bytes[2] = static_cast<std::char_traits<char>::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu));
+ utf8_bytes[3] = static_cast<std::char_traits<char>::int_type>(0x80u | (charcode & 0x3Fu));
+ utf8_bytes_filled = 4;
+ }
+ else
+ {
+ utf8_bytes[0] = static_cast<std::char_traits<char>::int_type>(wc);
+ utf8_bytes_filled = 1;
+ }
+ }
+ }
+ }
+};
+
+// Wraps another input adapter to convert wide character types into individual bytes.
+template<typename BaseInputAdapter, typename WideCharType>
+class wide_string_input_adapter
+{
+ public:
+ using char_type = char;
+
+ wide_string_input_adapter(BaseInputAdapter base)
+ : base_adapter(base) {}
+
+ typename std::char_traits<char>::int_type get_character() noexcept
+ {
+ // check if buffer needs to be filled
+ if (utf8_bytes_index == utf8_bytes_filled)
+ {
+ fill_buffer<sizeof(WideCharType)>();
+
+ JSON_ASSERT(utf8_bytes_filled > 0);
+ JSON_ASSERT(utf8_bytes_index == 0);
+ }
+
+ // use buffer
+ JSON_ASSERT(utf8_bytes_filled > 0);
+ JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled);
+ return utf8_bytes[utf8_bytes_index++];
+ }
+
+ // parsing binary with wchar doesn't make sense, but since the parsing mode can be runtime, we need something here
+ template<class T>
+ std::size_t get_elements(T* /*dest*/, std::size_t /*count*/ = 1)
+ {
+ JSON_THROW(parse_error::create(112, 1, "wide string type cannot be interpreted as binary data", nullptr));
+ }
+
+ private:
+ BaseInputAdapter base_adapter;
+
+ template<size_t T>
+ void fill_buffer()
+ {
+ wide_string_input_helper<BaseInputAdapter, T>::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);
+ }
+
+ /// a buffer for UTF-8 bytes
+ std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};
+
+ /// index to the utf8_codes array for the next valid byte
+ std::size_t utf8_bytes_index = 0;
+ /// number of valid bytes in the utf8_codes array
+ std::size_t utf8_bytes_filled = 0;
+};
+
+template<typename IteratorType, typename Enable = void>
+struct iterator_input_adapter_factory
+{
+ using iterator_type = IteratorType;
+ using char_type = typename std::iterator_traits<iterator_type>::value_type;
+ using adapter_type = iterator_input_adapter<iterator_type>;
+
+ static adapter_type create(IteratorType first, IteratorType last)
+ {
+ return adapter_type(std::move(first), std::move(last));
+ }
+};
+
+template<typename T>
+struct is_iterator_of_multibyte
+{
+ using value_type = typename std::iterator_traits<T>::value_type;
+ enum
+ {
+ value = sizeof(value_type) > 1
+ };
+};
+
+template<typename IteratorType>
+struct iterator_input_adapter_factory<IteratorType, enable_if_t<is_iterator_of_multibyte<IteratorType>::value>>
+{
+ using iterator_type = IteratorType;
+ using char_type = typename std::iterator_traits<iterator_type>::value_type;
+ using base_adapter_type = iterator_input_adapter<iterator_type>;
+ using adapter_type = wide_string_input_adapter<base_adapter_type, char_type>;
+
+ static adapter_type create(IteratorType first, IteratorType last)
+ {
+ return adapter_type(base_adapter_type(std::move(first), std::move(last)));
+ }
+};
+
+// General purpose iterator-based input
+template<typename IteratorType>
+typename iterator_input_adapter_factory<IteratorType>::adapter_type input_adapter(IteratorType first, IteratorType last)
+{
+ using factory_type = iterator_input_adapter_factory<IteratorType>;
+ return factory_type::create(first, last);
+}
+
+// Convenience shorthand from container to iterator
+// Enables ADL on begin(container) and end(container)
+// Encloses the using declarations in namespace for not to leak them to outside scope
+
+namespace container_input_adapter_factory_impl
+{
+
+using std::begin;
+using std::end;
+
+template<typename ContainerType, typename Enable = void>
+struct container_input_adapter_factory {};
+
+template<typename ContainerType>
+struct container_input_adapter_factory< ContainerType,
+ void_t<decltype(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>()))>>
+ {
+ using adapter_type = decltype(input_adapter(begin(std::declval<ContainerType>()), end(std::declval<ContainerType>())));
+
+ static adapter_type create(const ContainerType& container)
+{
+ return input_adapter(begin(container), end(container));
+}
+ };
+
+} // namespace container_input_adapter_factory_impl
+
+template<typename ContainerType>
+typename container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::adapter_type input_adapter(const ContainerType& container)
+{
+ return container_input_adapter_factory_impl::container_input_adapter_factory<ContainerType>::create(container);
+}
+
+// specialization for std::string
+using string_input_adapter_type = decltype(input_adapter(std::declval<std::string>()));
+
+#ifndef JSON_NO_IO
+// Special cases with fast paths
+inline file_input_adapter input_adapter(std::FILE* file)
+{
+ if (file == nullptr)
+ {
+ JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
+ }
+ return file_input_adapter(file);
+}
+
+inline input_stream_adapter input_adapter(std::istream& stream)
+{
+ return input_stream_adapter(stream);
+}
+
+inline input_stream_adapter input_adapter(std::istream&& stream)
+{
+ return input_stream_adapter(stream);
+}
+#endif // JSON_NO_IO
+
+using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval<const char*>(), std::declval<const char*>()));
+
+// Null-delimited strings, and the like.
+template < typename CharT,
+ typename std::enable_if <
+ std::is_pointer<CharT>::value&&
+ !std::is_array<CharT>::value&&
+ std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+ sizeof(typename std::remove_pointer<CharT>::type) == 1,
+ int >::type = 0 >
+contiguous_bytes_input_adapter input_adapter(CharT b)
+{
+ if (b == nullptr)
+ {
+ JSON_THROW(parse_error::create(101, 0, "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
+ }
+ auto length = std::strlen(reinterpret_cast<const char*>(b));
+ const auto* ptr = reinterpret_cast<const char*>(b);
+ return input_adapter(ptr, ptr + length); // cppcheck-suppress[nullPointerArithmeticRedundantCheck]
+}
+
+template<typename T, std::size_t N>
+auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+{
+ return input_adapter(array, array + N);
+}
+
+// This class only handles inputs of input_buffer_adapter type.
+// It's required so that expressions like {ptr, len} can be implicitly cast
+// to the correct adapter.
+class span_input_adapter
+{
+ public:
+ template < typename CharT,
+ typename std::enable_if <
+ std::is_pointer<CharT>::value&&
+ std::is_integral<typename std::remove_pointer<CharT>::type>::value&&
+ sizeof(typename std::remove_pointer<CharT>::type) == 1,
+ int >::type = 0 >
+ span_input_adapter(CharT b, std::size_t l)
+ : ia(reinterpret_cast<const char*>(b), reinterpret_cast<const char*>(b) + l) {}
+
+ template<class IteratorType,
+ typename std::enable_if<
+ std::is_same<typename iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
+ int>::type = 0>
+ span_input_adapter(IteratorType first, IteratorType last)
+ : ia(input_adapter(first, last)) {}
+
+ contiguous_bytes_input_adapter&& get()
+ {
+ return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg)
+ }
+
+ private:
+ contiguous_bytes_input_adapter ia;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/json_sax.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef>
+#include <string> // string
+#include <type_traits> // enable_if_t
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <clocale> // localeconv
+#include <cstddef> // size_t
+#include <cstdio> // snprintf
+#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull
+#include <initializer_list> // initializer_list
+#include <string> // char_traits, string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/position_t.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////
+// lexer //
+///////////
+
+template<typename BasicJsonType>
+class lexer_base
+{
+ public:
+ /// token types for the parser
+ enum class token_type
+ {
+ uninitialized, ///< indicating the scanner is uninitialized
+ literal_true, ///< the `true` literal
+ literal_false, ///< the `false` literal
+ literal_null, ///< the `null` literal
+ value_string, ///< a string -- use get_string() for actual value
+ value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value
+ value_integer, ///< a signed integer -- use get_number_integer() for actual value
+ value_float, ///< an floating point number -- use get_number_float() for actual value
+ begin_array, ///< the character for array begin `[`
+ begin_object, ///< the character for object begin `{`
+ end_array, ///< the character for array end `]`
+ end_object, ///< the character for object end `}`
+ name_separator, ///< the name separator `:`
+ value_separator, ///< the value separator `,`
+ parse_error, ///< indicating a parse error
+ end_of_input, ///< indicating the end of the input buffer
+ literal_or_value ///< a literal or the begin of a value (only for diagnostics)
+ };
+
+ /// return name of values of type token_type (only used for errors)
+ JSON_HEDLEY_RETURNS_NON_NULL
+ JSON_HEDLEY_CONST
+ static const char* token_type_name(const token_type t) noexcept
+ {
+ switch (t)
+ {
+ case token_type::uninitialized:
+ return "<uninitialized>";
+ case token_type::literal_true:
+ return "true literal";
+ case token_type::literal_false:
+ return "false literal";
+ case token_type::literal_null:
+ return "null literal";
+ case token_type::value_string:
+ return "string literal";
+ case token_type::value_unsigned:
+ case token_type::value_integer:
+ case token_type::value_float:
+ return "number literal";
+ case token_type::begin_array:
+ return "'['";
+ case token_type::begin_object:
+ return "'{'";
+ case token_type::end_array:
+ return "']'";
+ case token_type::end_object:
+ return "'}'";
+ case token_type::name_separator:
+ return "':'";
+ case token_type::value_separator:
+ return "','";
+ case token_type::parse_error:
+ return "<parse error>";
+ case token_type::end_of_input:
+ return "end of input";
+ case token_type::literal_or_value:
+ return "'[', '{', or a literal";
+ // LCOV_EXCL_START
+ default: // catch non-enum values
+ return "unknown token";
+ // LCOV_EXCL_STOP
+ }
+ }
+};
+/*!
+@brief lexical analysis
+
+This class organizes the lexical analysis during JSON deserialization.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class lexer : public lexer_base<BasicJsonType>
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using char_type = typename InputAdapterType::char_type;
+ using char_int_type = typename char_traits<char_type>::int_type;
+
+ public:
+ using token_type = typename lexer_base<BasicJsonType>::token_type;
+
+ explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept
+ : ia(std::move(adapter))
+ , ignore_comments(ignore_comments_)
+ , decimal_point_char(static_cast<char_int_type>(get_decimal_point()))
+ {}
+
+ // delete because of pointer members
+ lexer(const lexer&) = delete;
+ lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ lexer& operator=(lexer&) = delete;
+ lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ ~lexer() = default;
+
+ private:
+ /////////////////////
+ // locales
+ /////////////////////
+
+ /// return the locale-dependent decimal point
+ JSON_HEDLEY_PURE
+ static char get_decimal_point() noexcept
+ {
+ const auto* loc = localeconv();
+ JSON_ASSERT(loc != nullptr);
+ return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);
+ }
+
+ /////////////////////
+ // scan functions
+ /////////////////////
+
+ /*!
+ @brief get codepoint from 4 hex characters following `\u`
+
+ For input "\u c1 c2 c3 c4" the codepoint is:
+ (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4
+ = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)
+
+ Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'
+ must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The
+ conversion is done by subtracting the offset (0x30, 0x37, and 0x57)
+ between the ASCII value of the character and the desired integer value.
+
+ @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or
+ non-hex character)
+ */
+ int get_codepoint()
+ {
+ // this function only makes sense after reading `\u`
+ JSON_ASSERT(current == 'u');
+ int codepoint = 0;
+
+ const auto factors = { 12u, 8u, 4u, 0u };
+ for (const auto factor : factors)
+ {
+ get();
+
+ if (current >= '0' && current <= '9')
+ {
+ codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x30u) << factor);
+ }
+ else if (current >= 'A' && current <= 'F')
+ {
+ codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x37u) << factor);
+ }
+ else if (current >= 'a' && current <= 'f')
+ {
+ codepoint += static_cast<int>((static_cast<unsigned int>(current) - 0x57u) << factor);
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF);
+ return codepoint;
+ }
+
+ /*!
+ @brief check if the next byte(s) are inside a given range
+
+ Adds the current byte and, for each passed range, reads a new byte and
+ checks if it is inside the range. If a violation was detected, set up an
+ error message and return false. Otherwise, return true.
+
+ @param[in] ranges list of integers; interpreted as list of pairs of
+ inclusive lower and upper bound, respectively
+
+ @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,
+ 1, 2, or 3 pairs. This precondition is enforced by an assertion.
+
+ @return true if and only if no range violation was detected
+ */
+ bool next_byte_in_range(std::initializer_list<char_int_type> ranges)
+ {
+ JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6);
+ add(current);
+
+ for (auto range = ranges.begin(); range != ranges.end(); ++range)
+ {
+ get();
+ if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) // NOLINT(bugprone-inc-dec-in-conditions)
+ {
+ add(current);
+ }
+ else
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /*!
+ @brief scan a string literal
+
+ This function scans a string according to Sect. 7 of RFC 8259. While
+ scanning, bytes are escaped and copied into buffer token_buffer. Then the
+ function returns successfully, token_buffer is *not* null-terminated (as it
+ may contain \0 bytes), and token_buffer.size() is the number of bytes in the
+ string.
+
+ @return token_type::value_string if string could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note In case of errors, variable error_message contains a textual
+ description.
+ */
+ token_type scan_string()
+ {
+ // reset token_buffer (ignore opening quote)
+ reset();
+
+ // we entered the function by reading an open quote
+ JSON_ASSERT(current == '\"');
+
+ while (true)
+ {
+ // get next character
+ switch (get())
+ {
+ // end of file while parsing string
+ case char_traits<char_type>::eof():
+ {
+ error_message = "invalid string: missing closing quote";
+ return token_type::parse_error;
+ }
+
+ // closing quote
+ case '\"':
+ {
+ return token_type::value_string;
+ }
+
+ // escapes
+ case '\\':
+ {
+ switch (get())
+ {
+ // quotation mark
+ case '\"':
+ add('\"');
+ break;
+ // reverse solidus
+ case '\\':
+ add('\\');
+ break;
+ // solidus
+ case '/':
+ add('/');
+ break;
+ // backspace
+ case 'b':
+ add('\b');
+ break;
+ // form feed
+ case 'f':
+ add('\f');
+ break;
+ // line feed
+ case 'n':
+ add('\n');
+ break;
+ // carriage return
+ case 'r':
+ add('\r');
+ break;
+ // tab
+ case 't':
+ add('\t');
+ break;
+
+ // unicode escapes
+ case 'u':
+ {
+ const int codepoint1 = get_codepoint();
+ int codepoint = codepoint1; // start with codepoint1
+
+ if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if code point is a high surrogate
+ if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF)
+ {
+ // expect next \uxxxx entry
+ if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u'))
+ {
+ const int codepoint2 = get_codepoint();
+
+ if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1))
+ {
+ error_message = "invalid string: '\\u' must be followed by 4 hex digits";
+ return token_type::parse_error;
+ }
+
+ // check if codepoint2 is a low surrogate
+ if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF))
+ {
+ // overwrite codepoint
+ codepoint = static_cast<int>(
+ // high surrogate occupies the most significant 22 bits
+ (static_cast<unsigned int>(codepoint1) << 10u)
+ // low surrogate occupies the least significant 15 bits
+ + static_cast<unsigned int>(codepoint2)
+ // there is still the 0xD800, 0xDC00 and 0x10000 noise
+ // in the result, so we have to subtract with:
+ // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00
+ - 0x35FDC00u);
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF";
+ return token_type::parse_error;
+ }
+ }
+ else
+ {
+ if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF))
+ {
+ error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF";
+ return token_type::parse_error;
+ }
+ }
+
+ // result of the above calculation yields a proper codepoint
+ JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF);
+
+ // translate codepoint into bytes
+ if (codepoint < 0x80)
+ {
+ // 1-byte characters: 0xxxxxxx (ASCII)
+ add(static_cast<char_int_type>(codepoint));
+ }
+ else if (codepoint <= 0x7FF)
+ {
+ // 2-byte characters: 110xxxxx 10xxxxxx
+ add(static_cast<char_int_type>(0xC0u | (static_cast<unsigned int>(codepoint) >> 6u)));
+ add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+ }
+ else if (codepoint <= 0xFFFF)
+ {
+ // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx
+ add(static_cast<char_int_type>(0xE0u | (static_cast<unsigned int>(codepoint) >> 12u)));
+ add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+ add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+ }
+ else
+ {
+ // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+ add(static_cast<char_int_type>(0xF0u | (static_cast<unsigned int>(codepoint) >> 18u)));
+ add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 12u) & 0x3Fu)));
+ add(static_cast<char_int_type>(0x80u | ((static_cast<unsigned int>(codepoint) >> 6u) & 0x3Fu)));
+ add(static_cast<char_int_type>(0x80u | (static_cast<unsigned int>(codepoint) & 0x3Fu)));
+ }
+
+ break;
+ }
+
+ // other characters after escape
+ default:
+ error_message = "invalid string: forbidden character after backslash";
+ return token_type::parse_error;
+ }
+
+ break;
+ }
+
+ // invalid control characters
+ case 0x00:
+ {
+ error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000";
+ return token_type::parse_error;
+ }
+
+ case 0x01:
+ {
+ error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001";
+ return token_type::parse_error;
+ }
+
+ case 0x02:
+ {
+ error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002";
+ return token_type::parse_error;
+ }
+
+ case 0x03:
+ {
+ error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003";
+ return token_type::parse_error;
+ }
+
+ case 0x04:
+ {
+ error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004";
+ return token_type::parse_error;
+ }
+
+ case 0x05:
+ {
+ error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005";
+ return token_type::parse_error;
+ }
+
+ case 0x06:
+ {
+ error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006";
+ return token_type::parse_error;
+ }
+
+ case 0x07:
+ {
+ error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007";
+ return token_type::parse_error;
+ }
+
+ case 0x08:
+ {
+ error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b";
+ return token_type::parse_error;
+ }
+
+ case 0x09:
+ {
+ error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t";
+ return token_type::parse_error;
+ }
+
+ case 0x0A:
+ {
+ error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n";
+ return token_type::parse_error;
+ }
+
+ case 0x0B:
+ {
+ error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B";
+ return token_type::parse_error;
+ }
+
+ case 0x0C:
+ {
+ error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f";
+ return token_type::parse_error;
+ }
+
+ case 0x0D:
+ {
+ error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r";
+ return token_type::parse_error;
+ }
+
+ case 0x0E:
+ {
+ error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E";
+ return token_type::parse_error;
+ }
+
+ case 0x0F:
+ {
+ error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F";
+ return token_type::parse_error;
+ }
+
+ case 0x10:
+ {
+ error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010";
+ return token_type::parse_error;
+ }
+
+ case 0x11:
+ {
+ error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011";
+ return token_type::parse_error;
+ }
+
+ case 0x12:
+ {
+ error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012";
+ return token_type::parse_error;
+ }
+
+ case 0x13:
+ {
+ error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013";
+ return token_type::parse_error;
+ }
+
+ case 0x14:
+ {
+ error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014";
+ return token_type::parse_error;
+ }
+
+ case 0x15:
+ {
+ error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015";
+ return token_type::parse_error;
+ }
+
+ case 0x16:
+ {
+ error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016";
+ return token_type::parse_error;
+ }
+
+ case 0x17:
+ {
+ error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017";
+ return token_type::parse_error;
+ }
+
+ case 0x18:
+ {
+ error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018";
+ return token_type::parse_error;
+ }
+
+ case 0x19:
+ {
+ error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019";
+ return token_type::parse_error;
+ }
+
+ case 0x1A:
+ {
+ error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A";
+ return token_type::parse_error;
+ }
+
+ case 0x1B:
+ {
+ error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B";
+ return token_type::parse_error;
+ }
+
+ case 0x1C:
+ {
+ error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C";
+ return token_type::parse_error;
+ }
+
+ case 0x1D:
+ {
+ error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D";
+ return token_type::parse_error;
+ }
+
+ case 0x1E:
+ {
+ error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E";
+ return token_type::parse_error;
+ }
+
+ case 0x1F:
+ {
+ error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F";
+ return token_type::parse_error;
+ }
+
+ // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))
+ case 0x20:
+ case 0x21:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ {
+ add(current);
+ break;
+ }
+
+ // U+0080..U+07FF: bytes C2..DF 80..BF
+ case 0xC2:
+ case 0xC3:
+ case 0xC4:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xD0:
+ case 0xD1:
+ case 0xD2:
+ case 0xD3:
+ case 0xD4:
+ case 0xD5:
+ case 0xD6:
+ case 0xD7:
+ case 0xD8:
+ case 0xD9:
+ case 0xDA:
+ case 0xDB:
+ case 0xDC:
+ case 0xDD:
+ case 0xDE:
+ case 0xDF:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF})))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+0800..U+0FFF: bytes E0 A0..BF 80..BF
+ case 0xE0:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF
+ // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xEE:
+ case 0xEF:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+D000..U+D7FF: bytes ED 80..9F 80..BF
+ case 0xED:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF
+ case 0xF0:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF
+ case 0xF4:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))
+ {
+ return token_type::parse_error;
+ }
+ break;
+ }
+
+ // remaining bytes (80..C1 and F5..FF) are ill-formed
+ default:
+ {
+ error_message = "invalid string: ill-formed UTF-8 byte";
+ return token_type::parse_error;
+ }
+ }
+ }
+ }
+
+ /*!
+ * @brief scan a comment
+ * @return whether comment could be scanned successfully
+ */
+ bool scan_comment()
+ {
+ switch (get())
+ {
+ // single-line comments skip input until a newline or EOF is read
+ case '/':
+ {
+ while (true)
+ {
+ switch (get())
+ {
+ case '\n':
+ case '\r':
+ case char_traits<char_type>::eof():
+ case '\0':
+ return true;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // multi-line comments skip input until */ is read
+ case '*':
+ {
+ while (true)
+ {
+ switch (get())
+ {
+ case char_traits<char_type>::eof():
+ case '\0':
+ {
+ error_message = "invalid comment; missing closing '*/'";
+ return false;
+ }
+
+ case '*':
+ {
+ switch (get())
+ {
+ case '/':
+ return true;
+
+ default:
+ {
+ unget();
+ continue;
+ }
+ }
+ }
+
+ default:
+ continue;
+ }
+ }
+ }
+
+ // unexpected character after reading '/'
+ default:
+ {
+ error_message = "invalid comment; expecting '/' or '*' after '/'";
+ return false;
+ }
+ }
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ static void strtof(float& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtof(str, endptr);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ static void strtof(double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtod(str, endptr);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ static void strtof(long double& f, const char* str, char** endptr) noexcept
+ {
+ f = std::strtold(str, endptr);
+ }
+
+ /*!
+ @brief scan a number literal
+
+ This function scans a string according to Sect. 6 of RFC 8259.
+
+ The function is realized with a deterministic finite state machine derived
+ from the grammar described in RFC 8259. Starting in state "init", the
+ input is read and used to determined the next state. Only state "done"
+ accepts the number. State "error" is a trap state to model errors. In the
+ table below, "anything" means any character but the ones listed before.
+
+ state | 0 | 1-9 | e E | + | - | . | anything
+ ---------|----------|----------|----------|---------|---------|----------|-----------
+ init | zero | any1 | [error] | [error] | minus | [error] | [error]
+ minus | zero | any1 | [error] | [error] | [error] | [error] | [error]
+ zero | done | done | exponent | done | done | decimal1 | done
+ any1 | any1 | any1 | exponent | done | done | decimal1 | done
+ decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error]
+ decimal2 | decimal2 | decimal2 | exponent | done | done | done | done
+ exponent | any2 | any2 | [error] | sign | sign | [error] | [error]
+ sign | any2 | any2 | [error] | [error] | [error] | [error] | [error]
+ any2 | any2 | any2 | done | done | done | done | done
+
+ The state machine is realized with one label per state (prefixed with
+ "scan_number_") and `goto` statements between them. The state machine
+ contains cycles, but any cycle can be left when EOF is read. Therefore,
+ the function is guaranteed to terminate.
+
+ During scanning, the read bytes are stored in token_buffer. This string is
+ then converted to a signed integer, an unsigned integer, or a
+ floating-point number.
+
+ @return token_type::value_unsigned, token_type::value_integer, or
+ token_type::value_float if number could be successfully scanned,
+ token_type::parse_error otherwise
+
+ @note The scanner is independent of the current locale. Internally, the
+ locale's decimal point is used instead of `.` to work with the
+ locale-dependent converters.
+ */
+ token_type scan_number() // lgtm [cpp/use-of-goto] `goto` is used in this function to implement the number-parsing state machine described above. By design, any finite input will eventually reach the "done" state or return token_type::parse_error. In each intermediate state, 1 byte of the input is appended to the token_buffer vector, and only the already initialized variables token_buffer, number_type, and error_message are manipulated.
+ {
+ // reset token_buffer to store the number's bytes
+ reset();
+
+ // the type of the parsed number; initially set to unsigned; will be
+ // changed if minus sign, decimal point or exponent is read
+ token_type number_type = token_type::value_unsigned;
+
+ // state (init): we just found out we need to scan a number
+ switch (current)
+ {
+ case '-':
+ {
+ add(current);
+ goto scan_number_minus;
+ }
+
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ // all other characters are rejected outside scan_number()
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+
+scan_number_minus:
+ // state: we just parsed a leading minus sign
+ number_type = token_type::value_integer;
+ switch (get())
+ {
+ case '0':
+ {
+ add(current);
+ goto scan_number_zero;
+ }
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '-'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_zero:
+ // state: we just parse a zero (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '.':
+ {
+ add(decimal_point_char);
+ decimal_point_position = token_buffer.size() - 1;
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_any1:
+ // state: we just parsed a number 0-9 (maybe with a leading minus sign)
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any1;
+ }
+
+ case '.':
+ {
+ add(decimal_point_char);
+ decimal_point_position = token_buffer.size() - 1;
+ goto scan_number_decimal1;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_decimal1:
+ // state: we just parsed a decimal point
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after '.'";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_decimal2:
+ // we just parsed at least one number after a decimal point
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_decimal2;
+ }
+
+ case 'e':
+ case 'E':
+ {
+ add(current);
+ goto scan_number_exponent;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_exponent:
+ // we just parsed an exponent
+ number_type = token_type::value_float;
+ switch (get())
+ {
+ case '+':
+ case '-':
+ {
+ add(current);
+ goto scan_number_sign;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message =
+ "invalid number; expected '+', '-', or digit after exponent";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_sign:
+ // we just parsed an exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ {
+ error_message = "invalid number; expected digit after exponent sign";
+ return token_type::parse_error;
+ }
+ }
+
+scan_number_any2:
+ // we just parsed a number after the exponent or exponent sign
+ switch (get())
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ add(current);
+ goto scan_number_any2;
+ }
+
+ default:
+ goto scan_number_done;
+ }
+
+scan_number_done:
+ // unget the character after the number (we only read it to know that
+ // we are done scanning a number)
+ unget();
+
+ char* endptr = nullptr; // NOLINT(misc-const-correctness,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ errno = 0;
+
+ // try to parse integers first and fall back to floats
+ if (number_type == token_type::value_unsigned)
+ {
+ const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
+
+ // we checked the number format before
+ JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+ if (errno != ERANGE)
+ {
+ value_unsigned = static_cast<number_unsigned_t>(x);
+ if (value_unsigned == x)
+ {
+ return token_type::value_unsigned;
+ }
+ }
+ }
+ else if (number_type == token_type::value_integer)
+ {
+ const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
+
+ // we checked the number format before
+ JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+ if (errno != ERANGE)
+ {
+ value_integer = static_cast<number_integer_t>(x);
+ if (value_integer == x)
+ {
+ return token_type::value_integer;
+ }
+ }
+ }
+
+ // this code is reached if we parse a floating-point number or if an
+ // integer conversion above failed
+ strtof(value_float, token_buffer.data(), &endptr);
+
+ // we checked the number format before
+ JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
+
+ return token_type::value_float;
+ }
+
+ /*!
+ @param[in] literal_text the literal text to expect
+ @param[in] length the length of the passed literal text
+ @param[in] return_type the token type to return on success
+ */
+ JSON_HEDLEY_NON_NULL(2)
+ token_type scan_literal(const char_type* literal_text, const std::size_t length,
+ token_type return_type)
+ {
+ JSON_ASSERT(char_traits<char_type>::to_char_type(current) == literal_text[0]);
+ for (std::size_t i = 1; i < length; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(char_traits<char_type>::to_char_type(get()) != literal_text[i]))
+ {
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+ return return_type;
+ }
+
+ /////////////////////
+ // input management
+ /////////////////////
+
+ /// reset token_buffer; current character is beginning of token
+ void reset() noexcept
+ {
+ token_buffer.clear();
+ token_string.clear();
+ decimal_point_position = std::string::npos;
+ token_string.push_back(char_traits<char_type>::to_char_type(current));
+ }
+
+ /*
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a
+ `char_traits<char>::eof()` in that case. Stores the scanned characters
+ for use in error messages.
+
+ @return character read from the input
+ */
+ char_int_type get()
+ {
+ ++position.chars_read_total;
+ ++position.chars_read_current_line;
+
+ if (next_unget)
+ {
+ // just reset the next_unget variable and work with current
+ next_unget = false;
+ }
+ else
+ {
+ current = ia.get_character();
+ }
+
+ if (JSON_HEDLEY_LIKELY(current != char_traits<char_type>::eof()))
+ {
+ token_string.push_back(char_traits<char_type>::to_char_type(current));
+ }
+
+ if (current == '\n')
+ {
+ ++position.lines_read;
+ position.chars_read_current_line = 0;
+ }
+
+ return current;
+ }
+
+ /*!
+ @brief unget current character (read it again on next get)
+
+ We implement unget by setting variable next_unget to true. The input is not
+ changed - we just simulate ungetting by modifying chars_read_total,
+ chars_read_current_line, and token_string. The next call to get() will
+ behave as if the unget character is read again.
+ */
+ void unget()
+ {
+ next_unget = true;
+
+ --position.chars_read_total;
+
+ // in case we "unget" a newline, we have to also decrement the lines_read
+ if (position.chars_read_current_line == 0)
+ {
+ if (position.lines_read > 0)
+ {
+ --position.lines_read;
+ }
+ }
+ else
+ {
+ --position.chars_read_current_line;
+ }
+
+ if (JSON_HEDLEY_LIKELY(current != char_traits<char_type>::eof()))
+ {
+ JSON_ASSERT(!token_string.empty());
+ token_string.pop_back();
+ }
+ }
+
+ /// add a character to token_buffer
+ void add(char_int_type c)
+ {
+ token_buffer.push_back(static_cast<typename string_t::value_type>(c));
+ }
+
+ public:
+ /////////////////////
+ // value getters
+ /////////////////////
+
+ /// return integer value
+ constexpr number_integer_t get_number_integer() const noexcept
+ {
+ return value_integer;
+ }
+
+ /// return unsigned integer value
+ constexpr number_unsigned_t get_number_unsigned() const noexcept
+ {
+ return value_unsigned;
+ }
+
+ /// return floating-point value
+ constexpr number_float_t get_number_float() const noexcept
+ {
+ return value_float;
+ }
+
+ /// return current string value (implicitly resets the token; useful only once)
+ string_t& get_string()
+ {
+ // translate decimal points from locale back to '.' (#4084)
+ if (decimal_point_char != '.' && decimal_point_position != std::string::npos)
+ {
+ token_buffer[decimal_point_position] = '.';
+ }
+ return token_buffer;
+ }
+
+ /////////////////////
+ // diagnostics
+ /////////////////////
+
+ /// return position of last read token
+ constexpr position_t get_position() const noexcept
+ {
+ return position;
+ }
+
+ /// return the last read token (for errors only). Will never contain EOF
+ /// (an arbitrary value that is not a valid char value, often -1), because
+ /// 255 may legitimately occur. May contain NUL, which should be escaped.
+ std::string get_token_string() const
+ {
+ // escape control characters
+ std::string result;
+ for (const auto c : token_string)
+ {
+ if (static_cast<unsigned char>(c) <= '\x1F')
+ {
+ // escape control characters
+ std::array<char, 9> cs{{}};
+ static_cast<void>((std::snprintf)(cs.data(), cs.size(), "<U+%.4X>", static_cast<unsigned char>(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ result += cs.data();
+ }
+ else
+ {
+ // add character as is
+ result.push_back(static_cast<std::string::value_type>(c));
+ }
+ }
+
+ return result;
+ }
+
+ /// return syntax error message
+ JSON_HEDLEY_RETURNS_NON_NULL
+ constexpr const char* get_error_message() const noexcept
+ {
+ return error_message;
+ }
+
+ /////////////////////
+ // actual scanner
+ /////////////////////
+
+ /*!
+ @brief skip the UTF-8 byte order mark
+ @return true iff there is no BOM or the correct BOM has been skipped
+ */
+ bool skip_bom()
+ {
+ if (get() == 0xEF)
+ {
+ // check if we completely parse the BOM
+ return get() == 0xBB && get() == 0xBF;
+ }
+
+ // the first character is not the beginning of the BOM; unget it to
+ // process is later
+ unget();
+ return true;
+ }
+
+ void skip_whitespace()
+ {
+ do
+ {
+ get();
+ }
+ while (current == ' ' || current == '\t' || current == '\n' || current == '\r');
+ }
+
+ token_type scan()
+ {
+ // initially, skip the BOM
+ if (position.chars_read_total == 0 && !skip_bom())
+ {
+ error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given";
+ return token_type::parse_error;
+ }
+
+ // read next character and ignore whitespace
+ skip_whitespace();
+
+ // ignore comments
+ while (ignore_comments && current == '/')
+ {
+ if (!scan_comment())
+ {
+ return token_type::parse_error;
+ }
+
+ // skip following whitespace
+ skip_whitespace();
+ }
+
+ switch (current)
+ {
+ // structural characters
+ case '[':
+ return token_type::begin_array;
+ case ']':
+ return token_type::end_array;
+ case '{':
+ return token_type::begin_object;
+ case '}':
+ return token_type::end_object;
+ case ':':
+ return token_type::name_separator;
+ case ',':
+ return token_type::value_separator;
+
+ // literals
+ case 't':
+ {
+ std::array<char_type, 4> true_literal = {{static_cast<char_type>('t'), static_cast<char_type>('r'), static_cast<char_type>('u'), static_cast<char_type>('e')}};
+ return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true);
+ }
+ case 'f':
+ {
+ std::array<char_type, 5> false_literal = {{static_cast<char_type>('f'), static_cast<char_type>('a'), static_cast<char_type>('l'), static_cast<char_type>('s'), static_cast<char_type>('e')}};
+ return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false);
+ }
+ case 'n':
+ {
+ std::array<char_type, 4> null_literal = {{static_cast<char_type>('n'), static_cast<char_type>('u'), static_cast<char_type>('l'), static_cast<char_type>('l')}};
+ return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null);
+ }
+
+ // string
+ case '\"':
+ return scan_string();
+
+ // number
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return scan_number();
+
+ // end of input (the null byte is needed when parsing from
+ // string literals)
+ case '\0':
+ case char_traits<char_type>::eof():
+ return token_type::end_of_input;
+
+ // error
+ default:
+ error_message = "invalid literal";
+ return token_type::parse_error;
+ }
+ }
+
+ private:
+ /// input adapter
+ InputAdapterType ia;
+
+ /// whether comments should be ignored (true) or signaled as errors (false)
+ const bool ignore_comments = false;
+
+ /// the current character
+ char_int_type current = char_traits<char_type>::eof();
+
+ /// whether the next get() call should just return current
+ bool next_unget = false;
+
+ /// the start position of the current token
+ position_t position {};
+
+ /// raw input token string (for error messages)
+ std::vector<char_type> token_string {};
+
+ /// buffer for variable-length tokens (numbers, strings)
+ string_t token_buffer {};
+
+ /// a description of occurred lexer errors
+ const char* error_message = "";
+
+ // number values
+ number_integer_t value_integer = 0;
+ number_unsigned_t value_unsigned = 0;
+ number_float_t value_float = 0;
+
+ /// the decimal point
+ const char_int_type decimal_point_char = '.';
+ /// the position of the decimal point in the input
+ std::size_t decimal_point_position = std::string::npos;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief SAX interface
+
+This class describes the SAX interface used by @ref nlohmann::json::sax_parse.
+Each function is called in different situations while the input is parsed. The
+boolean return value informs the parser whether to continue processing the
+input.
+*/
+template<typename BasicJsonType>
+struct json_sax
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+
+ /*!
+ @brief a null value was read
+ @return whether parsing should proceed
+ */
+ virtual bool null() = 0;
+
+ /*!
+ @brief a boolean value was read
+ @param[in] val boolean value
+ @return whether parsing should proceed
+ */
+ virtual bool boolean(bool val) = 0;
+
+ /*!
+ @brief an integer number was read
+ @param[in] val integer value
+ @return whether parsing should proceed
+ */
+ virtual bool number_integer(number_integer_t val) = 0;
+
+ /*!
+ @brief an unsigned integer number was read
+ @param[in] val unsigned integer value
+ @return whether parsing should proceed
+ */
+ virtual bool number_unsigned(number_unsigned_t val) = 0;
+
+ /*!
+ @brief a floating-point number was read
+ @param[in] val floating-point value
+ @param[in] s raw token value
+ @return whether parsing should proceed
+ */
+ virtual bool number_float(number_float_t val, const string_t& s) = 0;
+
+ /*!
+ @brief a string value was read
+ @param[in] val string value
+ @return whether parsing should proceed
+ @note It is safe to move the passed string value.
+ */
+ virtual bool string(string_t& val) = 0;
+
+ /*!
+ @brief a binary value was read
+ @param[in] val binary value
+ @return whether parsing should proceed
+ @note It is safe to move the passed binary value.
+ */
+ virtual bool binary(binary_t& val) = 0;
+
+ /*!
+ @brief the beginning of an object was read
+ @param[in] elements number of object elements or -1 if unknown
+ @return whether parsing should proceed
+ @note binary formats may report the number of elements
+ */
+ virtual bool start_object(std::size_t elements) = 0;
+
+ /*!
+ @brief an object key was read
+ @param[in] val object key
+ @return whether parsing should proceed
+ @note It is safe to move the passed string.
+ */
+ virtual bool key(string_t& val) = 0;
+
+ /*!
+ @brief the end of an object was read
+ @return whether parsing should proceed
+ */
+ virtual bool end_object() = 0;
+
+ /*!
+ @brief the beginning of an array was read
+ @param[in] elements number of array elements or -1 if unknown
+ @return whether parsing should proceed
+ @note binary formats may report the number of elements
+ */
+ virtual bool start_array(std::size_t elements) = 0;
+
+ /*!
+ @brief the end of an array was read
+ @return whether parsing should proceed
+ */
+ virtual bool end_array() = 0;
+
+ /*!
+ @brief a parse error occurred
+ @param[in] position the position in the input where the error occurs
+ @param[in] last_token the last read token
+ @param[in] ex an exception object describing the error
+ @return whether parsing should proceed (must return false)
+ */
+ virtual bool parse_error(std::size_t position,
+ const std::string& last_token,
+ const detail::exception& ex) = 0;
+
+ json_sax() = default;
+ json_sax(const json_sax&) = default;
+ json_sax(json_sax&&) noexcept = default;
+ json_sax& operator=(const json_sax&) = default;
+ json_sax& operator=(json_sax&&) noexcept = default;
+ virtual ~json_sax() = default;
+};
+
+namespace detail
+{
+constexpr std::size_t unknown_size()
+{
+ return (std::numeric_limits<std::size_t>::max)();
+}
+
+/*!
+@brief SAX implementation to create a JSON value from SAX events
+
+This class implements the @ref json_sax interface and processes the SAX events
+to create a JSON value which makes it basically a DOM parser. The structure or
+hierarchy of the JSON value is managed by the stack `ref_stack` which contains
+a pointer to the respective array or object for each recursion depth.
+
+After successful parsing, the value that is passed by reference to the
+constructor contains the parsed value.
+
+@tparam BasicJsonType the JSON type
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class json_sax_dom_parser
+{
+ public:
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using lexer_t = lexer<BasicJsonType, InputAdapterType>;
+
+ /*!
+ @param[in,out] r reference to a JSON value that is manipulated while
+ parsing
+ @param[in] allow_exceptions_ whether parse errors yield exceptions
+ */
+ explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true, lexer_t* lexer_ = nullptr)
+ : root(r), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_)
+ {}
+
+ // make class move-only
+ json_sax_dom_parser(const json_sax_dom_parser&) = delete;
+ json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete;
+ json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ ~json_sax_dom_parser() = default;
+
+ bool null()
+ {
+ handle_value(nullptr);
+ return true;
+ }
+
+ bool boolean(bool val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_integer(number_integer_t val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_unsigned(number_unsigned_t val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_float(number_float_t val, const string_t& /*unused*/)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool string(string_t& val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool binary(binary_t& val)
+ {
+ handle_value(std::move(val));
+ return true;
+ }
+
+ bool start_object(std::size_t len)
+ {
+ ref_stack.push_back(handle_value(BasicJsonType::value_t::object));
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ // Manually set the start position of the object here.
+ // Ensure this is after the call to handle_value to ensure correct start position.
+ if (m_lexer_ref)
+ {
+ // Lexer has read the first character of the object, so
+ // subtract 1 from the position to get the correct start position.
+ ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
+ }
+#endif
+
+ if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
+ {
+ JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+ }
+
+ return true;
+ }
+
+ bool key(string_t& val)
+ {
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(ref_stack.back()->is_object());
+
+ // add null at given key and store the reference for later
+ object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val));
+ return true;
+ }
+
+ bool end_object()
+ {
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(ref_stack.back()->is_object());
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ if (m_lexer_ref)
+ {
+ // Lexer's position is past the closing brace, so set that as the end position.
+ ref_stack.back()->end_position = m_lexer_ref->get_position();
+ }
+#endif
+
+ ref_stack.back()->set_parents();
+ ref_stack.pop_back();
+ return true;
+ }
+
+ bool start_array(std::size_t len)
+ {
+ ref_stack.push_back(handle_value(BasicJsonType::value_t::array));
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ // Manually set the start position of the array here.
+ // Ensure this is after the call to handle_value to ensure correct start position.
+ if (m_lexer_ref)
+ {
+ ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
+ }
+#endif
+
+ if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
+ {
+ JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+ }
+
+ return true;
+ }
+
+ bool end_array()
+ {
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(ref_stack.back()->is_array());
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ if (m_lexer_ref)
+ {
+ // Lexer's position is past the closing bracket, so set that as the end position.
+ ref_stack.back()->end_position = m_lexer_ref->get_position();
+ }
+#endif
+
+ ref_stack.back()->set_parents();
+ ref_stack.pop_back();
+ return true;
+ }
+
+ template<class Exception>
+ bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+ const Exception& ex)
+ {
+ errored = true;
+ static_cast<void>(ex);
+ if (allow_exceptions)
+ {
+ JSON_THROW(ex);
+ }
+ return false;
+ }
+
+ constexpr bool is_errored() const
+ {
+ return errored;
+ }
+
+ private:
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ void handle_diagnostic_positions_for_json_value(BasicJsonType& v)
+ {
+ if (m_lexer_ref)
+ {
+ // Lexer has read past the current field value, so set the end position to the current position.
+ // The start position will be set below based on the length of the string representation
+ // of the value.
+ v.end_position = m_lexer_ref->get_position();
+
+ switch (v.type())
+ {
+ case value_t::boolean:
+ {
+ // 4 and 5 are the string length of "true" and "false"
+ v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5);
+ break;
+ }
+
+ case value_t::null:
+ {
+ // 4 is the string length of "null"
+ v.start_position = v.end_position - 4;
+ break;
+ }
+
+ case value_t::string:
+ {
+ // include the length of the quotes, which is 2
+ v.start_position = v.end_position - v.m_data.m_value.string->size() - 2;
+ break;
+ }
+
+ // As we handle the start and end positions for values created during parsing,
+ // we do not expect the following value type to be called. Regardless, set the positions
+ // in case this is created manually or through a different constructor. Exclude from lcov
+ // since the exact condition of this switch is esoteric.
+ // LCOV_EXCL_START
+ case value_t::discarded:
+ {
+ v.end_position = std::string::npos;
+ v.start_position = v.end_position;
+ break;
+ }
+ // LCOV_EXCL_STOP
+ case value_t::binary:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ {
+ v.start_position = v.end_position - m_lexer_ref->get_string().size();
+ break;
+ }
+ case value_t::object:
+ case value_t::array:
+ {
+ // object and array are handled in start_object() and start_array() handlers
+ // skip setting the values here.
+ break;
+ }
+ default: // LCOV_EXCL_LINE
+ // Handle all possible types discretely, default handler should never be reached.
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE
+ }
+ }
+ }
+#endif
+
+ /*!
+ @invariant If the ref stack is empty, then the passed value will be the new
+ root.
+ @invariant If the ref stack contains a value, then it is an array or an
+ object to which we can add elements
+ */
+ template<typename Value>
+ JSON_HEDLEY_RETURNS_NON_NULL
+ BasicJsonType* handle_value(Value&& v)
+ {
+ if (ref_stack.empty())
+ {
+ root = BasicJsonType(std::forward<Value>(v));
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ handle_diagnostic_positions_for_json_value(root);
+#endif
+
+ return &root;
+ }
+
+ JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+ if (ref_stack.back()->is_array())
+ {
+ ref_stack.back()->m_data.m_value.array->emplace_back(std::forward<Value>(v));
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ handle_diagnostic_positions_for_json_value(ref_stack.back()->m_data.m_value.array->back());
+#endif
+
+ return &(ref_stack.back()->m_data.m_value.array->back());
+ }
+
+ JSON_ASSERT(ref_stack.back()->is_object());
+ JSON_ASSERT(object_element);
+ *object_element = BasicJsonType(std::forward<Value>(v));
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ handle_diagnostic_positions_for_json_value(*object_element);
+#endif
+
+ return object_element;
+ }
+
+ /// the parsed JSON value
+ BasicJsonType& root;
+ /// stack to model hierarchy of values
+ std::vector<BasicJsonType*> ref_stack {};
+ /// helper to hold the reference for the next object element
+ BasicJsonType* object_element = nullptr;
+ /// whether a syntax error occurred
+ bool errored = false;
+ /// whether to throw exceptions in case of errors
+ const bool allow_exceptions = true;
+ /// the lexer reference to obtain the current position
+ lexer_t* m_lexer_ref = nullptr;
+};
+
+template<typename BasicJsonType, typename InputAdapterType>
+class json_sax_dom_callback_parser
+{
+ public:
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using parser_callback_t = typename BasicJsonType::parser_callback_t;
+ using parse_event_t = typename BasicJsonType::parse_event_t;
+ using lexer_t = lexer<BasicJsonType, InputAdapterType>;
+
+ json_sax_dom_callback_parser(BasicJsonType& r,
+ parser_callback_t cb,
+ const bool allow_exceptions_ = true,
+ lexer_t* lexer_ = nullptr)
+ : root(r), callback(std::move(cb)), allow_exceptions(allow_exceptions_), m_lexer_ref(lexer_)
+ {
+ keep_stack.push_back(true);
+ }
+
+ // make class move-only
+ json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete;
+ json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete;
+ json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ ~json_sax_dom_callback_parser() = default;
+
+ bool null()
+ {
+ handle_value(nullptr);
+ return true;
+ }
+
+ bool boolean(bool val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_integer(number_integer_t val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_unsigned(number_unsigned_t val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool number_float(number_float_t val, const string_t& /*unused*/)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool string(string_t& val)
+ {
+ handle_value(val);
+ return true;
+ }
+
+ bool binary(binary_t& val)
+ {
+ handle_value(std::move(val));
+ return true;
+ }
+
+ bool start_object(std::size_t len)
+ {
+ // check callback for object start
+ const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);
+ keep_stack.push_back(keep);
+
+ auto val = handle_value(BasicJsonType::value_t::object, true);
+ ref_stack.push_back(val.second);
+
+ if (ref_stack.back())
+ {
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ // Manually set the start position of the object here.
+ // Ensure this is after the call to handle_value to ensure correct start position.
+ if (m_lexer_ref)
+ {
+ // Lexer has read the first character of the object, so
+ // subtract 1 from the position to get the correct start position.
+ ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
+ }
+#endif
+
+ // check object limit
+ if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
+ {
+ JSON_THROW(out_of_range::create(408, concat("excessive object size: ", std::to_string(len)), ref_stack.back()));
+ }
+ }
+ return true;
+ }
+
+ bool key(string_t& val)
+ {
+ BasicJsonType k = BasicJsonType(val);
+
+ // check callback for key
+ const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);
+ key_keep_stack.push_back(keep);
+
+ // add discarded value at given key and store the reference for later
+ if (keep && ref_stack.back())
+ {
+ object_element = &(ref_stack.back()->m_data.m_value.object->operator[](val) = discarded);
+ }
+
+ return true;
+ }
+
+ bool end_object()
+ {
+ if (ref_stack.back())
+ {
+ if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
+ {
+ // discard object
+ *ref_stack.back() = discarded;
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ // Set start/end positions for discarded object.
+ handle_diagnostic_positions_for_json_value(*ref_stack.back());
+#endif
+ }
+ else
+ {
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ if (m_lexer_ref)
+ {
+ // Lexer's position is past the closing brace, so set that as the end position.
+ ref_stack.back()->end_position = m_lexer_ref->get_position();
+ }
+#endif
+
+ ref_stack.back()->set_parents();
+ }
+ }
+
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(!keep_stack.empty());
+ ref_stack.pop_back();
+ keep_stack.pop_back();
+
+ if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured())
+ {
+ // remove discarded value
+ for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)
+ {
+ if (it->is_discarded())
+ {
+ ref_stack.back()->erase(it);
+ break;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool start_array(std::size_t len)
+ {
+ const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);
+ keep_stack.push_back(keep);
+
+ auto val = handle_value(BasicJsonType::value_t::array, true);
+ ref_stack.push_back(val.second);
+
+ if (ref_stack.back())
+ {
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ // Manually set the start position of the array here.
+ // Ensure this is after the call to handle_value to ensure correct start position.
+ if (m_lexer_ref)
+ {
+ // Lexer has read the first character of the array, so
+ // subtract 1 from the position to get the correct start position.
+ ref_stack.back()->start_position = m_lexer_ref->get_position() - 1;
+ }
+#endif
+
+ // check array limit
+ if (JSON_HEDLEY_UNLIKELY(len != detail::unknown_size() && len > ref_stack.back()->max_size()))
+ {
+ JSON_THROW(out_of_range::create(408, concat("excessive array size: ", std::to_string(len)), ref_stack.back()));
+ }
+ }
+
+ return true;
+ }
+
+ bool end_array()
+ {
+ bool keep = true;
+
+ if (ref_stack.back())
+ {
+ keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
+ if (keep)
+ {
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ if (m_lexer_ref)
+ {
+ // Lexer's position is past the closing bracket, so set that as the end position.
+ ref_stack.back()->end_position = m_lexer_ref->get_position();
+ }
+#endif
+
+ ref_stack.back()->set_parents();
+ }
+ else
+ {
+ // discard array
+ *ref_stack.back() = discarded;
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ // Set start/end positions for discarded array.
+ handle_diagnostic_positions_for_json_value(*ref_stack.back());
+#endif
+ }
+ }
+
+ JSON_ASSERT(!ref_stack.empty());
+ JSON_ASSERT(!keep_stack.empty());
+ ref_stack.pop_back();
+ keep_stack.pop_back();
+
+ // remove discarded value
+ if (!keep && !ref_stack.empty() && ref_stack.back()->is_array())
+ {
+ ref_stack.back()->m_data.m_value.array->pop_back();
+ }
+
+ return true;
+ }
+
+ template<class Exception>
+ bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,
+ const Exception& ex)
+ {
+ errored = true;
+ static_cast<void>(ex);
+ if (allow_exceptions)
+ {
+ JSON_THROW(ex);
+ }
+ return false;
+ }
+
+ constexpr bool is_errored() const
+ {
+ return errored;
+ }
+
+ private:
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ void handle_diagnostic_positions_for_json_value(BasicJsonType& v)
+ {
+ if (m_lexer_ref)
+ {
+ // Lexer has read past the current field value, so set the end position to the current position.
+ // The start position will be set below based on the length of the string representation
+ // of the value.
+ v.end_position = m_lexer_ref->get_position();
+
+ switch (v.type())
+ {
+ case value_t::boolean:
+ {
+ // 4 and 5 are the string length of "true" and "false"
+ v.start_position = v.end_position - (v.m_data.m_value.boolean ? 4 : 5);
+ break;
+ }
+
+ case value_t::null:
+ {
+ // 4 is the string length of "null"
+ v.start_position = v.end_position - 4;
+ break;
+ }
+
+ case value_t::string:
+ {
+ // include the length of the quotes, which is 2
+ v.start_position = v.end_position - v.m_data.m_value.string->size() - 2;
+ break;
+ }
+
+ case value_t::discarded:
+ {
+ v.end_position = std::string::npos;
+ v.start_position = v.end_position;
+ break;
+ }
+
+ case value_t::binary:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ {
+ v.start_position = v.end_position - m_lexer_ref->get_string().size();
+ break;
+ }
+
+ case value_t::object:
+ case value_t::array:
+ {
+ // object and array are handled in start_object() and start_array() handlers
+ // skip setting the values here.
+ break;
+ }
+ default: // LCOV_EXCL_LINE
+ // Handle all possible types discretely, default handler should never be reached.
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert,-warnings-as-errors) LCOV_EXCL_LINE
+ }
+ }
+ }
+#endif
+
+ /*!
+ @param[in] v value to add to the JSON value we build during parsing
+ @param[in] skip_callback whether we should skip calling the callback
+ function; this is required after start_array() and
+ start_object() SAX events, because otherwise we would call the
+ callback function with an empty array or object, respectively.
+
+ @invariant If the ref stack is empty, then the passed value will be the new
+ root.
+ @invariant If the ref stack contains a value, then it is an array or an
+ object to which we can add elements
+
+ @return pair of boolean (whether value should be kept) and pointer (to the
+ passed value in the ref_stack hierarchy; nullptr if not kept)
+ */
+ template<typename Value>
+ std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)
+ {
+ JSON_ASSERT(!keep_stack.empty());
+
+ // do not handle this value if we know it would be added to a discarded
+ // container
+ if (!keep_stack.back())
+ {
+ return {false, nullptr};
+ }
+
+ // create value
+ auto value = BasicJsonType(std::forward<Value>(v));
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ handle_diagnostic_positions_for_json_value(value);
+#endif
+
+ // check callback
+ const bool keep = skip_callback || callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);
+
+ // do not handle this value if we just learnt it shall be discarded
+ if (!keep)
+ {
+ return {false, nullptr};
+ }
+
+ if (ref_stack.empty())
+ {
+ root = std::move(value);
+ return {true, & root};
+ }
+
+ // skip this value if we already decided to skip the parent
+ // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)
+ if (!ref_stack.back())
+ {
+ return {false, nullptr};
+ }
+
+ // we now only expect arrays and objects
+ JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object());
+
+ // array
+ if (ref_stack.back()->is_array())
+ {
+ ref_stack.back()->m_data.m_value.array->emplace_back(std::move(value));
+ return {true, & (ref_stack.back()->m_data.m_value.array->back())};
+ }
+
+ // object
+ JSON_ASSERT(ref_stack.back()->is_object());
+ // check if we should store an element for the current key
+ JSON_ASSERT(!key_keep_stack.empty());
+ const bool store_element = key_keep_stack.back();
+ key_keep_stack.pop_back();
+
+ if (!store_element)
+ {
+ return {false, nullptr};
+ }
+
+ JSON_ASSERT(object_element);
+ *object_element = std::move(value);
+ return {true, object_element};
+ }
+
+ /// the parsed JSON value
+ BasicJsonType& root;
+ /// stack to model hierarchy of values
+ std::vector<BasicJsonType*> ref_stack {};
+ /// stack to manage which values to keep
+ std::vector<bool> keep_stack {}; // NOLINT(readability-redundant-member-init)
+ /// stack to manage which object keys to keep
+ std::vector<bool> key_keep_stack {}; // NOLINT(readability-redundant-member-init)
+ /// helper to hold the reference for the next object element
+ BasicJsonType* object_element = nullptr;
+ /// whether a syntax error occurred
+ bool errored = false;
+ /// callback function
+ const parser_callback_t callback = nullptr;
+ /// whether to throw exceptions in case of errors
+ const bool allow_exceptions = true;
+ /// a discarded value for the callback
+ BasicJsonType discarded = BasicJsonType::value_t::discarded;
+ /// the lexer reference to obtain the current position
+ lexer_t* m_lexer_ref = nullptr;
+};
+
+template<typename BasicJsonType>
+class json_sax_acceptor
+{
+ public:
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+
+ bool null()
+ {
+ return true;
+ }
+
+ bool boolean(bool /*unused*/)
+ {
+ return true;
+ }
+
+ bool number_integer(number_integer_t /*unused*/)
+ {
+ return true;
+ }
+
+ bool number_unsigned(number_unsigned_t /*unused*/)
+ {
+ return true;
+ }
+
+ bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)
+ {
+ return true;
+ }
+
+ bool string(string_t& /*unused*/)
+ {
+ return true;
+ }
+
+ bool binary(binary_t& /*unused*/)
+ {
+ return true;
+ }
+
+ bool start_object(std::size_t /*unused*/ = detail::unknown_size())
+ {
+ return true;
+ }
+
+ bool key(string_t& /*unused*/)
+ {
+ return true;
+ }
+
+ bool end_object()
+ {
+ return true;
+ }
+
+ bool start_array(std::size_t /*unused*/ = detail::unknown_size())
+ {
+ return true;
+ }
+
+ bool end_array()
+ {
+ return true;
+ }
+
+ bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)
+ {
+ return false;
+ }
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/is_sax.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstdint> // size_t
+#include <utility> // declval
+#include <string> // string
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/detected.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename T>
+using null_function_t = decltype(std::declval<T&>().null());
+
+template<typename T>
+using boolean_function_t =
+ decltype(std::declval<T&>().boolean(std::declval<bool>()));
+
+template<typename T, typename Integer>
+using number_integer_function_t =
+ decltype(std::declval<T&>().number_integer(std::declval<Integer>()));
+
+template<typename T, typename Unsigned>
+using number_unsigned_function_t =
+ decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));
+
+template<typename T, typename Float, typename String>
+using number_float_function_t = decltype(std::declval<T&>().number_float(
+ std::declval<Float>(), std::declval<const String&>()));
+
+template<typename T, typename String>
+using string_function_t =
+ decltype(std::declval<T&>().string(std::declval<String&>()));
+
+template<typename T, typename Binary>
+using binary_function_t =
+ decltype(std::declval<T&>().binary(std::declval<Binary&>()));
+
+template<typename T>
+using start_object_function_t =
+ decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));
+
+template<typename T, typename String>
+using key_function_t =
+ decltype(std::declval<T&>().key(std::declval<String&>()));
+
+template<typename T>
+using end_object_function_t = decltype(std::declval<T&>().end_object());
+
+template<typename T>
+using start_array_function_t =
+ decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));
+
+template<typename T>
+using end_array_function_t = decltype(std::declval<T&>().end_array());
+
+template<typename T, typename Exception>
+using parse_error_function_t = decltype(std::declval<T&>().parse_error(
+ std::declval<std::size_t>(), std::declval<const std::string&>(),
+ std::declval<const Exception&>()));
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax
+{
+ private:
+ static_assert(is_basic_json<BasicJsonType>::value,
+ "BasicJsonType must be of type basic_json<...>");
+
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using exception_t = typename BasicJsonType::exception;
+
+ public:
+ static constexpr bool value =
+ is_detected_exact<bool, null_function_t, SAX>::value &&
+ is_detected_exact<bool, boolean_function_t, SAX>::value &&
+ is_detected_exact<bool, number_integer_function_t, SAX, number_integer_t>::value &&
+ is_detected_exact<bool, number_unsigned_function_t, SAX, number_unsigned_t>::value &&
+ is_detected_exact<bool, number_float_function_t, SAX, number_float_t, string_t>::value &&
+ is_detected_exact<bool, string_function_t, SAX, string_t>::value &&
+ is_detected_exact<bool, binary_function_t, SAX, binary_t>::value &&
+ is_detected_exact<bool, start_object_function_t, SAX>::value &&
+ is_detected_exact<bool, key_function_t, SAX, string_t>::value &&
+ is_detected_exact<bool, end_object_function_t, SAX>::value &&
+ is_detected_exact<bool, start_array_function_t, SAX>::value &&
+ is_detected_exact<bool, end_array_function_t, SAX>::value &&
+ is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;
+};
+
+template<typename SAX, typename BasicJsonType>
+struct is_sax_static_asserts
+{
+ private:
+ static_assert(is_basic_json<BasicJsonType>::value,
+ "BasicJsonType must be of type basic_json<...>");
+
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using exception_t = typename BasicJsonType::exception;
+
+ public:
+ static_assert(is_detected_exact<bool, null_function_t, SAX>::value,
+ "Missing/invalid function: bool null()");
+ static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+ "Missing/invalid function: bool boolean(bool)");
+ static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,
+ "Missing/invalid function: bool boolean(bool)");
+ static_assert(
+ is_detected_exact<bool, number_integer_function_t, SAX,
+ number_integer_t>::value,
+ "Missing/invalid function: bool number_integer(number_integer_t)");
+ static_assert(
+ is_detected_exact<bool, number_unsigned_function_t, SAX,
+ number_unsigned_t>::value,
+ "Missing/invalid function: bool number_unsigned(number_unsigned_t)");
+ static_assert(is_detected_exact<bool, number_float_function_t, SAX,
+ number_float_t, string_t>::value,
+ "Missing/invalid function: bool number_float(number_float_t, const string_t&)");
+ static_assert(
+ is_detected_exact<bool, string_function_t, SAX, string_t>::value,
+ "Missing/invalid function: bool string(string_t&)");
+ static_assert(
+ is_detected_exact<bool, binary_function_t, SAX, binary_t>::value,
+ "Missing/invalid function: bool binary(binary_t&)");
+ static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,
+ "Missing/invalid function: bool start_object(std::size_t)");
+ static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,
+ "Missing/invalid function: bool key(string_t&)");
+ static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,
+ "Missing/invalid function: bool end_object()");
+ static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,
+ "Missing/invalid function: bool start_array(std::size_t)");
+ static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,
+ "Missing/invalid function: bool end_array()");
+ static_assert(
+ is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,
+ "Missing/invalid function: bool parse_error(std::size_t, const "
+ "std::string&, const exception&)");
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// how to treat CBOR tags
+enum class cbor_tag_handler_t
+{
+ error, ///< throw a parse_error exception in case of a tag
+ ignore, ///< ignore tags
+ store ///< store tags as binary type
+};
+
+/*!
+@brief determine system byte order
+
+@return true if and only if system's byte order is little endian
+
+@note from https://stackoverflow.com/a/1001328/266378
+*/
+static inline bool little_endianness(int num = 1) noexcept
+{
+ return *reinterpret_cast<char*>(&num) == 1;
+}
+
+///////////////////
+// binary reader //
+///////////////////
+
+/*!
+@brief deserialization of CBOR, MessagePack, and UBJSON values
+*/
+template<typename BasicJsonType, typename InputAdapterType, typename SAX = json_sax_dom_parser<BasicJsonType, InputAdapterType>>
+class binary_reader
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using json_sax_t = SAX;
+ using char_type = typename InputAdapterType::char_type;
+ using char_int_type = typename char_traits<char_type>::int_type;
+
+ public:
+ /*!
+ @brief create a binary reader
+
+ @param[in] adapter input adapter to read from
+ */
+ explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json) noexcept : ia(std::move(adapter)), input_format(format)
+ {
+ (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+ }
+
+ // make class move-only
+ binary_reader(const binary_reader&) = delete;
+ binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ binary_reader& operator=(const binary_reader&) = delete;
+ binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor)
+ ~binary_reader() = default;
+
+ /*!
+ @param[in] format the binary format to parse
+ @param[in] sax_ a SAX event processor
+ @param[in] strict whether to expect the input to be consumed completed
+ @param[in] tag_handler how to treat CBOR tags
+
+ @return whether parsing was successful
+ */
+ JSON_HEDLEY_NON_NULL(3)
+ bool sax_parse(const input_format_t format,
+ json_sax_t* sax_,
+ const bool strict = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ sax = sax_;
+ bool result = false;
+
+ switch (format)
+ {
+ case input_format_t::bson:
+ result = parse_bson_internal();
+ break;
+
+ case input_format_t::cbor:
+ result = parse_cbor_internal(true, tag_handler);
+ break;
+
+ case input_format_t::msgpack:
+ result = parse_msgpack_internal();
+ break;
+
+ case input_format_t::ubjson:
+ case input_format_t::bjdata:
+ result = parse_ubjson_internal();
+ break;
+
+ case input_format_t::json: // LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+
+ // strict mode: next byte must be EOF
+ if (result && strict)
+ {
+ if (input_format == input_format_t::ubjson || input_format == input_format_t::bjdata)
+ {
+ get_ignore_noop();
+ }
+ else
+ {
+ get();
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(current != char_traits<char_type>::eof()))
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(110, chars_read,
+ exception_message(input_format, concat("expected end of input; last byte: 0x", get_token_string()), "value"), nullptr));
+ }
+ }
+
+ return result;
+ }
+
+ private:
+ //////////
+ // BSON //
+ //////////
+
+ /*!
+ @brief Reads in a BSON-object and passes it to the SAX-parser.
+ @return whether a valid BSON-value was passed to the SAX parser
+ */
+ bool parse_bson_internal()
+ {
+ std::int32_t document_size{};
+ get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false)))
+ {
+ return false;
+ }
+
+ return sax->end_object();
+ }
+
+ /*!
+ @brief Parses a C-style string from the BSON input.
+ @param[in,out] result A reference to the string variable where the read
+ string is to be stored.
+ @return `true` if the \x00-byte indicating the end of the string was
+ encountered before the EOF; false` indicates an unexpected EOF.
+ */
+ bool get_bson_cstr(string_t& result)
+ {
+ auto out = std::back_inserter(result);
+ while (true)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring")))
+ {
+ return false;
+ }
+ if (current == 0x00)
+ {
+ return true;
+ }
+ *out++ = static_cast<typename string_t::value_type>(current);
+ }
+ }
+
+ /*!
+ @brief Parses a zero-terminated string of length @a len from the BSON
+ input.
+ @param[in] len The length (including the zero-byte at the end) of the
+ string to be read.
+ @param[in,out] result A reference to the string variable where the read
+ string is to be stored.
+ @tparam NumberType The type of the length @a len
+ @pre len >= 1
+ @return `true` if the string was successfully parsed
+ */
+ template<typename NumberType>
+ bool get_bson_string(const NumberType len, string_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(len < 1))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::bson, concat("string length must be at least 1, is ", std::to_string(len)), "string"), nullptr));
+ }
+
+ return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != char_traits<char_type>::eof();
+ }
+
+ /*!
+ @brief Parses a byte array input of length @a len from the BSON input.
+ @param[in] len The length of the byte array to be read.
+ @param[in,out] result A reference to the binary variable where the read
+ array is to be stored.
+ @tparam NumberType The type of the length @a len
+ @pre len >= 0
+ @return `true` if the byte array was successfully parsed
+ */
+ template<typename NumberType>
+ bool get_bson_binary(const NumberType len, binary_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(len < 0))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::bson, concat("byte array length cannot be negative, is ", std::to_string(len)), "binary"), nullptr));
+ }
+
+ // All BSON binary values have a subtype
+ std::uint8_t subtype{};
+ get_number<std::uint8_t>(input_format_t::bson, subtype);
+ result.set_subtype(subtype);
+
+ return get_binary(input_format_t::bson, len, result);
+ }
+
+ /*!
+ @brief Read a BSON document element of the given @a element_type.
+ @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html
+ @param[in] element_type_parse_position The position in the input stream,
+ where the `element_type` was read.
+ @warning Not all BSON element types are supported yet. An unsupported
+ @a element_type will give rise to a parse_error.114:
+ Unsupported BSON record type 0x...
+ @return whether a valid BSON-object/array was passed to the SAX parser
+ */
+ bool parse_bson_element_internal(const char_int_type element_type,
+ const std::size_t element_type_parse_position)
+ {
+ switch (element_type)
+ {
+ case 0x01: // double
+ {
+ double number{};
+ return get_number<double, true>(input_format_t::bson, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 0x02: // string
+ {
+ std::int32_t len{};
+ string_t value;
+ return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value);
+ }
+
+ case 0x03: // object
+ {
+ return parse_bson_internal();
+ }
+
+ case 0x04: // array
+ {
+ return parse_bson_array();
+ }
+
+ case 0x05: // binary
+ {
+ std::int32_t len{};
+ binary_t value;
+ return get_number<std::int32_t, true>(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value);
+ }
+
+ case 0x08: // boolean
+ {
+ return sax->boolean(get() != 0);
+ }
+
+ case 0x0A: // null
+ {
+ return sax->null();
+ }
+
+ case 0x10: // int32
+ {
+ std::int32_t value{};
+ return get_number<std::int32_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+ }
+
+ case 0x12: // int64
+ {
+ std::int64_t value{};
+ return get_number<std::int64_t, true>(input_format_t::bson, value) && sax->number_integer(value);
+ }
+
+ case 0x11: // uint64
+ {
+ std::uint64_t value{};
+ return get_number<std::uint64_t, true>(input_format_t::bson, value) && sax->number_unsigned(value);
+ }
+
+ default: // anything else not supported (yet)
+ {
+ std::array<char, 3> cr{{}};
+ static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ const std::string cr_str{cr.data()};
+ return sax->parse_error(element_type_parse_position, cr_str,
+ parse_error::create(114, element_type_parse_position, concat("Unsupported BSON record type 0x", cr_str), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief Read a BSON element list (as specified in the BSON-spec)
+
+ The same binary layout is used for objects and arrays, hence it must be
+ indicated with the argument @a is_array which one is expected
+ (true --> array, false --> object).
+
+ @param[in] is_array Determines if the element list being read is to be
+ treated as an object (@a is_array == false), or as an
+ array (@a is_array == true).
+ @return whether a valid BSON-object/array was passed to the SAX parser
+ */
+ bool parse_bson_element_list(const bool is_array)
+ {
+ string_t key;
+
+ while (auto element_type = get())
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list")))
+ {
+ return false;
+ }
+
+ const std::size_t element_type_parse_position = chars_read;
+ if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key)))
+ {
+ return false;
+ }
+
+ if (!is_array && !sax->key(key))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position)))
+ {
+ return false;
+ }
+
+ // get_bson_cstr only appends
+ key.clear();
+ }
+
+ return true;
+ }
+
+ /*!
+ @brief Reads an array from the BSON input and passes it to the SAX-parser.
+ @return whether a valid BSON-array was passed to the SAX parser
+ */
+ bool parse_bson_array()
+ {
+ std::int32_t document_size{};
+ get_number<std::int32_t, true>(input_format_t::bson, document_size);
+
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true)))
+ {
+ return false;
+ }
+
+ return sax->end_array();
+ }
+
+ //////////
+ // CBOR //
+ //////////
+
+ /*!
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true) or whether the last read character should
+ be considered instead (false)
+ @param[in] tag_handler how CBOR tags should be treated
+
+ @return whether a valid CBOR value was passed to the SAX parser
+ */
+ bool parse_cbor_internal(const bool get_char,
+ const cbor_tag_handler_t tag_handler)
+ {
+ switch (get_char ? get() : current)
+ {
+ // EOF
+ case char_traits<char_type>::eof():
+ return unexpect_eof(input_format_t::cbor, "value");
+
+ // Integer 0x00..0x17 (0..23)
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ return sax->number_unsigned(static_cast<number_unsigned_t>(current));
+
+ case 0x18: // Unsigned integer (one-byte uint8_t follows)
+ {
+ std::uint8_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+ }
+
+ case 0x19: // Unsigned integer (two-byte uint16_t follows)
+ {
+ std::uint16_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+ }
+
+ case 0x1A: // Unsigned integer (four-byte uint32_t follows)
+ {
+ std::uint32_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+ }
+
+ case 0x1B: // Unsigned integer (eight-byte uint64_t follows)
+ {
+ std::uint64_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_unsigned(number);
+ }
+
+ // Negative integer -1-0x00..-1-0x17 (-1..-24)
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ return sax->number_integer(static_cast<std::int8_t>(0x20 - 1 - current));
+
+ case 0x38: // Negative integer (one-byte uint8_t follows)
+ {
+ std::uint8_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+ }
+
+ case 0x39: // Negative integer -1-n (two-byte uint16_t follows)
+ {
+ std::uint16_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+ }
+
+ case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)
+ {
+ std::uint32_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1) - number);
+ }
+
+ case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)
+ {
+ std::uint64_t number{};
+ return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast<number_integer_t>(-1)
+ - static_cast<number_integer_t>(number));
+ }
+
+ // Binary data (0x00..0x17 bytes follow)
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58: // Binary data (one-byte uint8_t for n follows)
+ case 0x59: // Binary data (two-byte uint16_t for n follow)
+ case 0x5A: // Binary data (four-byte uint32_t for n follow)
+ case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+ case 0x5F: // Binary data (indefinite length)
+ {
+ binary_t b;
+ return get_cbor_binary(b) && sax->binary(b);
+ }
+
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ string_t s;
+ return get_cbor_string(s) && sax->string(s);
+ }
+
+ // array (0x00..0x17 data items follow)
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ return get_cbor_array(
+ conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+
+ case 0x98: // array (one-byte uint8_t for n follows)
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0x99: // array (two-byte uint16_t for n follow)
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0x9A: // array (four-byte uint32_t for n follow)
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0x9B: // array (eight-byte uint64_t for n follow)
+ {
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_array(conditional_static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0x9F: // array (indefinite length)
+ return get_cbor_array(detail::unknown_size(), tag_handler);
+
+ // map (0x00..0x17 pairs of data items follow)
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ return get_cbor_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x1Fu), tag_handler);
+
+ case 0xB8: // map (one-byte uint8_t for n follows)
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0xB9: // map (two-byte uint16_t for n follow)
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0xBA: // map (four-byte uint32_t for n follow)
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0xBB: // map (eight-byte uint64_t for n follow)
+ {
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) && get_cbor_object(conditional_static_cast<std::size_t>(len), tag_handler);
+ }
+
+ case 0xBF: // map (indefinite length)
+ return get_cbor_object(detail::unknown_size(), tag_handler);
+
+ case 0xC6: // tagged item
+ case 0xC7:
+ case 0xC8:
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCC:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xD0:
+ case 0xD1:
+ case 0xD2:
+ case 0xD3:
+ case 0xD4:
+ case 0xD8: // tagged item (1 bytes follow)
+ case 0xD9: // tagged item (2 bytes follow)
+ case 0xDA: // tagged item (4 bytes follow)
+ case 0xDB: // tagged item (8 bytes follow)
+ {
+ switch (tag_handler)
+ {
+ case cbor_tag_handler_t::error:
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+ }
+
+ case cbor_tag_handler_t::ignore:
+ {
+ // ignore binary subtype
+ switch (current)
+ {
+ case 0xD8:
+ {
+ std::uint8_t subtype_to_ignore{};
+ get_number(input_format_t::cbor, subtype_to_ignore);
+ break;
+ }
+ case 0xD9:
+ {
+ std::uint16_t subtype_to_ignore{};
+ get_number(input_format_t::cbor, subtype_to_ignore);
+ break;
+ }
+ case 0xDA:
+ {
+ std::uint32_t subtype_to_ignore{};
+ get_number(input_format_t::cbor, subtype_to_ignore);
+ break;
+ }
+ case 0xDB:
+ {
+ std::uint64_t subtype_to_ignore{};
+ get_number(input_format_t::cbor, subtype_to_ignore);
+ break;
+ }
+ default:
+ break;
+ }
+ return parse_cbor_internal(true, tag_handler);
+ }
+
+ case cbor_tag_handler_t::store:
+ {
+ binary_t b;
+ // use binary subtype and store in binary container
+ switch (current)
+ {
+ case 0xD8:
+ {
+ std::uint8_t subtype{};
+ get_number(input_format_t::cbor, subtype);
+ b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+ break;
+ }
+ case 0xD9:
+ {
+ std::uint16_t subtype{};
+ get_number(input_format_t::cbor, subtype);
+ b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+ break;
+ }
+ case 0xDA:
+ {
+ std::uint32_t subtype{};
+ get_number(input_format_t::cbor, subtype);
+ b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+ break;
+ }
+ case 0xDB:
+ {
+ std::uint64_t subtype{};
+ get_number(input_format_t::cbor, subtype);
+ b.set_subtype(detail::conditional_static_cast<typename binary_t::subtype_type>(subtype));
+ break;
+ }
+ default:
+ return parse_cbor_internal(true, tag_handler);
+ }
+ get();
+ return get_cbor_binary(b) && sax->binary(b);
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ return false; // LCOV_EXCL_LINE
+ }
+ }
+
+ case 0xF4: // false
+ return sax->boolean(false);
+
+ case 0xF5: // true
+ return sax->boolean(true);
+
+ case 0xF6: // null
+ return sax->null();
+
+ case 0xF9: // Half-Precision Float (two-byte IEEE 754)
+ {
+ const auto byte1_raw = get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+ {
+ return false;
+ }
+ const auto byte2_raw = get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number")))
+ {
+ return false;
+ }
+
+ const auto byte1 = static_cast<unsigned char>(byte1_raw);
+ const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+ // code from RFC 7049, Appendix D, Figure 3:
+ // As half-precision floating-point numbers were only added
+ // to IEEE 754 in 2008, today's programming platforms often
+ // still only have limited support for them. It is very
+ // easy to include at least decoding support for them even
+ // without such support. An example of a small decoder for
+ // half-precision floating-point numbers in the C language
+ // is shown in Fig. 3.
+ const auto half = static_cast<unsigned int>((byte1 << 8u) + byte2);
+ const double val = [&half]
+ {
+ const int exp = (half >> 10u) & 0x1Fu;
+ const unsigned int mant = half & 0x3FFu;
+ JSON_ASSERT(0 <= exp&& exp <= 32);
+ JSON_ASSERT(mant <= 1024);
+ switch (exp)
+ {
+ case 0:
+ return std::ldexp(mant, -24);
+ case 31:
+ return (mant == 0)
+ ? std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::quiet_NaN();
+ default:
+ return std::ldexp(mant + 1024, exp - 25);
+ }
+ }();
+ return sax->number_float((half & 0x8000u) != 0
+ ? static_cast<number_float_t>(-val)
+ : static_cast<number_float_t>(val), "");
+ }
+
+ case 0xFA: // Single-Precision Float (four-byte IEEE 754)
+ {
+ float number{};
+ return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 0xFB: // Double-Precision Float (eight-byte IEEE 754)
+ {
+ double number{};
+ return get_number(input_format_t::cbor, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ default: // anything else (0xFF is handled inside the other types)
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::cbor, concat("invalid byte: 0x", last_token), "value"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief reads a CBOR string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+ Additionally, CBOR's strings with indefinite lengths are supported.
+
+ @param[out] result created string
+
+ @return whether string creation completed
+ */
+ bool get_cbor_string(string_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string")))
+ {
+ return false;
+ }
+
+ switch (current)
+ {
+ // UTF-8 string (0x00..0x17 bytes follow)
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ {
+ return get_string(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+ }
+
+ case 0x78: // UTF-8 string (one-byte uint8_t for n follows)
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+ }
+
+ case 0x79: // UTF-8 string (two-byte uint16_t for n follow)
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+ }
+
+ case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+ }
+
+ case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)
+ {
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result);
+ }
+
+ case 0x7F: // UTF-8 string (indefinite length)
+ {
+ while (get() != 0xFF)
+ {
+ string_t chunk;
+ if (!get_cbor_string(chunk))
+ {
+ return false;
+ }
+ result.append(chunk);
+ }
+ return true;
+ }
+
+ default:
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+ exception_message(input_format_t::cbor, concat("expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x", last_token), "string"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief reads a CBOR byte array
+
+ This function first reads starting bytes to determine the expected
+ byte array length and then copies this number of bytes into the byte array.
+ Additionally, CBOR's byte arrays with indefinite lengths are supported.
+
+ @param[out] result created byte array
+
+ @return whether byte array creation completed
+ */
+ bool get_cbor_binary(binary_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary")))
+ {
+ return false;
+ }
+
+ switch (current)
+ {
+ // Binary data (0x00..0x17 bytes follow)
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ {
+ return get_binary(input_format_t::cbor, static_cast<unsigned int>(current) & 0x1Fu, result);
+ }
+
+ case 0x58: // Binary data (one-byte uint8_t for n follows)
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::cbor, len) &&
+ get_binary(input_format_t::cbor, len, result);
+ }
+
+ case 0x59: // Binary data (two-byte uint16_t for n follow)
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::cbor, len) &&
+ get_binary(input_format_t::cbor, len, result);
+ }
+
+ case 0x5A: // Binary data (four-byte uint32_t for n follow)
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::cbor, len) &&
+ get_binary(input_format_t::cbor, len, result);
+ }
+
+ case 0x5B: // Binary data (eight-byte uint64_t for n follow)
+ {
+ std::uint64_t len{};
+ return get_number(input_format_t::cbor, len) &&
+ get_binary(input_format_t::cbor, len, result);
+ }
+
+ case 0x5F: // Binary data (indefinite length)
+ {
+ while (get() != 0xFF)
+ {
+ binary_t chunk;
+ if (!get_cbor_binary(chunk))
+ {
+ return false;
+ }
+ result.insert(result.end(), chunk.begin(), chunk.end());
+ }
+ return true;
+ }
+
+ default:
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+ exception_message(input_format_t::cbor, concat("expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x", last_token), "binary"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @param[in] len the length of the array or detail::unknown_size() for an
+ array of indefinite size
+ @param[in] tag_handler how CBOR tags should be treated
+ @return whether array creation completed
+ */
+ bool get_cbor_array(const std::size_t len,
+ const cbor_tag_handler_t tag_handler)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+ {
+ return false;
+ }
+
+ if (len != detail::unknown_size())
+ {
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ while (get() != 0xFF)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler)))
+ {
+ return false;
+ }
+ }
+ }
+
+ return sax->end_array();
+ }
+
+ /*!
+ @param[in] len the length of the object or detail::unknown_size() for an
+ object of indefinite size
+ @param[in] tag_handler how CBOR tags should be treated
+ @return whether object creation completed
+ */
+ bool get_cbor_object(const std::size_t len,
+ const cbor_tag_handler_t tag_handler)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+ {
+ return false;
+ }
+
+ if (len != 0)
+ {
+ string_t key;
+ if (len != detail::unknown_size())
+ {
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+ {
+ return false;
+ }
+ key.clear();
+ }
+ }
+ else
+ {
+ while (get() != 0xFF)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler)))
+ {
+ return false;
+ }
+ key.clear();
+ }
+ }
+ }
+
+ return sax->end_object();
+ }
+
+ /////////////
+ // MsgPack //
+ /////////////
+
+ /*!
+ @return whether a valid MessagePack value was passed to the SAX parser
+ */
+ bool parse_msgpack_internal()
+ {
+ switch (get())
+ {
+ // EOF
+ case char_traits<char_type>::eof():
+ return unexpect_eof(input_format_t::msgpack, "value");
+
+ // positive fixint
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x0E:
+ case 0x0F:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1A:
+ case 0x1B:
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ case 0x2C:
+ case 0x2D:
+ case 0x2E:
+ case 0x2F:
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3A:
+ case 0x3B:
+ case 0x3C:
+ case 0x3D:
+ case 0x3E:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B:
+ case 0x4C:
+ case 0x4D:
+ case 0x4E:
+ case 0x4F:
+ case 0x50:
+ case 0x51:
+ case 0x52:
+ case 0x53:
+ case 0x54:
+ case 0x55:
+ case 0x56:
+ case 0x57:
+ case 0x58:
+ case 0x59:
+ case 0x5A:
+ case 0x5B:
+ case 0x5C:
+ case 0x5D:
+ case 0x5E:
+ case 0x5F:
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6A:
+ case 0x6B:
+ case 0x6C:
+ case 0x6D:
+ case 0x6E:
+ case 0x6F:
+ case 0x70:
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ return sax->number_unsigned(static_cast<number_unsigned_t>(current));
+
+ // fixmap
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x85:
+ case 0x86:
+ case 0x87:
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ return get_msgpack_object(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+ // fixarray
+ case 0x90:
+ case 0x91:
+ case 0x92:
+ case 0x93:
+ case 0x94:
+ case 0x95:
+ case 0x96:
+ case 0x97:
+ case 0x98:
+ case 0x99:
+ case 0x9A:
+ case 0x9B:
+ case 0x9C:
+ case 0x9D:
+ case 0x9E:
+ case 0x9F:
+ return get_msgpack_array(conditional_static_cast<std::size_t>(static_cast<unsigned int>(current) & 0x0Fu));
+
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ case 0xD9: // str 8
+ case 0xDA: // str 16
+ case 0xDB: // str 32
+ {
+ string_t s;
+ return get_msgpack_string(s) && sax->string(s);
+ }
+
+ case 0xC0: // nil
+ return sax->null();
+
+ case 0xC2: // false
+ return sax->boolean(false);
+
+ case 0xC3: // true
+ return sax->boolean(true);
+
+ case 0xC4: // bin 8
+ case 0xC5: // bin 16
+ case 0xC6: // bin 32
+ case 0xC7: // ext 8
+ case 0xC8: // ext 16
+ case 0xC9: // ext 32
+ case 0xD4: // fixext 1
+ case 0xD5: // fixext 2
+ case 0xD6: // fixext 4
+ case 0xD7: // fixext 8
+ case 0xD8: // fixext 16
+ {
+ binary_t b;
+ return get_msgpack_binary(b) && sax->binary(b);
+ }
+
+ case 0xCA: // float 32
+ {
+ float number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 0xCB: // float 64
+ {
+ double number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 0xCC: // uint 8
+ {
+ std::uint8_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+ }
+
+ case 0xCD: // uint 16
+ {
+ std::uint16_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+ }
+
+ case 0xCE: // uint 32
+ {
+ std::uint32_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+ }
+
+ case 0xCF: // uint 64
+ {
+ std::uint64_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number);
+ }
+
+ case 0xD0: // int 8
+ {
+ std::int8_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+ }
+
+ case 0xD1: // int 16
+ {
+ std::int16_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+ }
+
+ case 0xD2: // int 32
+ {
+ std::int32_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+ }
+
+ case 0xD3: // int 64
+ {
+ std::int64_t number{};
+ return get_number(input_format_t::msgpack, number) && sax->number_integer(number);
+ }
+
+ case 0xDC: // array 16
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast<std::size_t>(len));
+ }
+
+ case 0xDD: // array 32
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_array(conditional_static_cast<std::size_t>(len));
+ }
+
+ case 0xDE: // map 16
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast<std::size_t>(len));
+ }
+
+ case 0xDF: // map 32
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) && get_msgpack_object(conditional_static_cast<std::size_t>(len));
+ }
+
+ // negative fixint
+ case 0xE0:
+ case 0xE1:
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE5:
+ case 0xE6:
+ case 0xE7:
+ case 0xE8:
+ case 0xE9:
+ case 0xEA:
+ case 0xEB:
+ case 0xEC:
+ case 0xED:
+ case 0xEE:
+ case 0xEF:
+ case 0xF0:
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ case 0xF4:
+ case 0xF5:
+ case 0xF6:
+ case 0xF7:
+ case 0xF8:
+ case 0xF9:
+ case 0xFA:
+ case 0xFB:
+ case 0xFC:
+ case 0xFD:
+ case 0xFE:
+ case 0xFF:
+ return sax->number_integer(static_cast<std::int8_t>(current));
+
+ default: // anything else
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format_t::msgpack, concat("invalid byte: 0x", last_token), "value"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief reads a MessagePack string
+
+ This function first reads starting bytes to determine the expected
+ string length and then copies this number of bytes into a string.
+
+ @param[out] result created string
+
+ @return whether string creation completed
+ */
+ bool get_msgpack_string(string_t& result)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string")))
+ {
+ return false;
+ }
+
+ switch (current)
+ {
+ // fixstr
+ case 0xA0:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ case 0xA4:
+ case 0xA5:
+ case 0xA6:
+ case 0xA7:
+ case 0xA8:
+ case 0xA9:
+ case 0xAA:
+ case 0xAB:
+ case 0xAC:
+ case 0xAD:
+ case 0xAE:
+ case 0xAF:
+ case 0xB0:
+ case 0xB1:
+ case 0xB2:
+ case 0xB3:
+ case 0xB4:
+ case 0xB5:
+ case 0xB6:
+ case 0xB7:
+ case 0xB8:
+ case 0xB9:
+ case 0xBA:
+ case 0xBB:
+ case 0xBC:
+ case 0xBD:
+ case 0xBE:
+ case 0xBF:
+ {
+ return get_string(input_format_t::msgpack, static_cast<unsigned int>(current) & 0x1Fu, result);
+ }
+
+ case 0xD9: // str 8
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+ }
+
+ case 0xDA: // str 16
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+ }
+
+ case 0xDB: // str 32
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result);
+ }
+
+ default:
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+ exception_message(input_format_t::msgpack, concat("expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x", last_token), "string"), nullptr));
+ }
+ }
+ }
+
+ /*!
+ @brief reads a MessagePack byte array
+
+ This function first reads starting bytes to determine the expected
+ byte array length and then copies this number of bytes into a byte array.
+
+ @param[out] result created byte array
+
+ @return whether byte array creation completed
+ */
+ bool get_msgpack_binary(binary_t& result)
+ {
+ // helper function to set the subtype
+ auto assign_and_return_true = [&result](std::int8_t subtype)
+ {
+ result.set_subtype(static_cast<std::uint8_t>(subtype));
+ return true;
+ };
+
+ switch (current)
+ {
+ case 0xC4: // bin 8
+ {
+ std::uint8_t len{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_binary(input_format_t::msgpack, len, result);
+ }
+
+ case 0xC5: // bin 16
+ {
+ std::uint16_t len{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_binary(input_format_t::msgpack, len, result);
+ }
+
+ case 0xC6: // bin 32
+ {
+ std::uint32_t len{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_binary(input_format_t::msgpack, len, result);
+ }
+
+ case 0xC7: // ext 8
+ {
+ std::uint8_t len{};
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, len, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xC8: // ext 16
+ {
+ std::uint16_t len{};
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, len, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xC9: // ext 32
+ {
+ std::uint32_t len{};
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, len) &&
+ get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, len, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD4: // fixext 1
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 1, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD5: // fixext 2
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 2, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD6: // fixext 4
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 4, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD7: // fixext 8
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 8, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ case 0xD8: // fixext 16
+ {
+ std::int8_t subtype{};
+ return get_number(input_format_t::msgpack, subtype) &&
+ get_binary(input_format_t::msgpack, 16, result) &&
+ assign_and_return_true(subtype);
+ }
+
+ default: // LCOV_EXCL_LINE
+ return false; // LCOV_EXCL_LINE
+ }
+ }
+
+ /*!
+ @param[in] len the length of the array
+ @return whether array creation completed
+ */
+ bool get_msgpack_array(const std::size_t len)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len)))
+ {
+ return false;
+ }
+
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+ {
+ return false;
+ }
+ }
+
+ return sax->end_array();
+ }
+
+ /*!
+ @param[in] len the length of the object
+ @return whether object creation completed
+ */
+ bool get_msgpack_object(const std::size_t len)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len)))
+ {
+ return false;
+ }
+
+ string_t key;
+ for (std::size_t i = 0; i < len; ++i)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal()))
+ {
+ return false;
+ }
+ key.clear();
+ }
+
+ return sax->end_object();
+ }
+
+ ////////////
+ // UBJSON //
+ ////////////
+
+ /*!
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+
+ @return whether a valid UBJSON value was passed to the SAX parser
+ */
+ bool parse_ubjson_internal(const bool get_char = true)
+ {
+ return get_ubjson_value(get_char ? get_ignore_noop() : current);
+ }
+
+ /*!
+ @brief reads a UBJSON string
+
+ This function is either called after reading the 'S' byte explicitly
+ indicating a string, or in case of an object key where the 'S' byte can be
+ left out.
+
+ @param[out] result created string
+ @param[in] get_char whether a new character should be retrieved from the
+ input (true, default) or whether the last read
+ character should be considered instead
+
+ @return whether string creation completed
+ */
+ bool get_ubjson_string(string_t& result, const bool get_char = true)
+ {
+ if (get_char)
+ {
+ get(); // TODO(niels): may we ignore N here?
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+ {
+ return false;
+ }
+
+ switch (current)
+ {
+ case 'U':
+ {
+ std::uint8_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'i':
+ {
+ std::int8_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'I':
+ {
+ std::int16_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'l':
+ {
+ std::int32_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'L':
+ {
+ std::int64_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'u':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint16_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'm':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint32_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ case 'M':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint64_t len{};
+ return get_number(input_format, len) && get_string(input_format, len, result);
+ }
+
+ default:
+ break;
+ }
+ auto last_token = get_token_string();
+ std::string message;
+
+ if (input_format != input_format_t::bjdata)
+ {
+ message = "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token;
+ }
+ else
+ {
+ message = "expected length type specification (U, i, u, I, m, l, M, L); last byte: 0x" + last_token;
+ }
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "string"), nullptr));
+ }
+
+ /*!
+ @param[out] dim an integer vector storing the ND array dimensions
+ @return whether reading ND array size vector is successful
+ */
+ bool get_ubjson_ndarray_size(std::vector<size_t>& dim)
+ {
+ std::pair<std::size_t, char_int_type> size_and_type;
+ size_t dimlen = 0;
+ bool no_ndarray = true;
+
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type, no_ndarray)))
+ {
+ return false;
+ }
+
+ if (size_and_type.first != npos)
+ {
+ if (size_and_type.second != 0)
+ {
+ if (size_and_type.second != 'N')
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, size_and_type.second)))
+ {
+ return false;
+ }
+ dim.push_back(dimlen);
+ }
+ }
+ }
+ else
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray)))
+ {
+ return false;
+ }
+ dim.push_back(dimlen);
+ }
+ }
+ }
+ else
+ {
+ while (current != ']')
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_value(dimlen, no_ndarray, current)))
+ {
+ return false;
+ }
+ dim.push_back(dimlen);
+ get_ignore_noop();
+ }
+ }
+ return true;
+ }
+
+ /*!
+ @param[out] result determined size
+ @param[in,out] is_ndarray for input, `true` means already inside an ndarray vector
+ or ndarray dimension is not allowed; `false` means ndarray
+ is allowed; for output, `true` means an ndarray is found;
+ is_ndarray can only return `true` when its initial value
+ is `false`
+ @param[in] prefix type marker if already read, otherwise set to 0
+
+ @return whether size determination completed
+ */
+ bool get_ubjson_size_value(std::size_t& result, bool& is_ndarray, char_int_type prefix = 0)
+ {
+ if (prefix == 0)
+ {
+ prefix = get_ignore_noop();
+ }
+
+ switch (prefix)
+ {
+ case 'U':
+ {
+ std::uint8_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'i':
+ {
+ std::int8_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (number < 0)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+ exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+ }
+ result = static_cast<std::size_t>(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char
+ return true;
+ }
+
+ case 'I':
+ {
+ std::int16_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (number < 0)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+ exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'l':
+ {
+ std::int32_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (number < 0)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+ exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'L':
+ {
+ std::int64_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (number < 0)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read,
+ exception_message(input_format, "count in an optimized container must be positive", "size"), nullptr));
+ }
+ if (!value_in_range_of<std::size_t>(number))
+ {
+ return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+ exception_message(input_format, "integer value overflow", "size"), nullptr));
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'u':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint16_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ result = static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'm':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint32_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ result = conditional_static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case 'M':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint64_t number{};
+ if (JSON_HEDLEY_UNLIKELY(!get_number(input_format, number)))
+ {
+ return false;
+ }
+ if (!value_in_range_of<std::size_t>(number))
+ {
+ return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408,
+ exception_message(input_format, "integer value overflow", "size"), nullptr));
+ }
+ result = detail::conditional_static_cast<std::size_t>(number);
+ return true;
+ }
+
+ case '[':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ if (is_ndarray) // ndarray dimensional vector can only contain integers, and can not embed another array
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(113, chars_read, exception_message(input_format, "ndarray dimensional vector is not allowed", "size"), nullptr));
+ }
+ std::vector<size_t> dim;
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_ndarray_size(dim)))
+ {
+ return false;
+ }
+ if (dim.size() == 1 || (dim.size() == 2 && dim.at(0) == 1)) // return normal array size if 1D row vector
+ {
+ result = dim.at(dim.size() - 1);
+ return true;
+ }
+ if (!dim.empty()) // if ndarray, convert to an object in JData annotated array format
+ {
+ for (auto i : dim) // test if any dimension in an ndarray is 0, if so, return a 1D empty container
+ {
+ if ( i == 0 )
+ {
+ result = 0;
+ return true;
+ }
+ }
+
+ string_t key = "_ArraySize_";
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(3) || !sax->key(key) || !sax->start_array(dim.size())))
+ {
+ return false;
+ }
+ result = 1;
+ for (auto i : dim)
+ {
+ result *= i;
+ if (result == 0 || result == npos) // because dim elements shall not have zeros, result = 0 means overflow happened; it also can't be npos as it is used to initialize size in get_ubjson_size_type()
+ {
+ return sax->parse_error(chars_read, get_token_string(), out_of_range::create(408, exception_message(input_format, "excessive ndarray size caused overflow", "size"), nullptr));
+ }
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(static_cast<number_unsigned_t>(i))))
+ {
+ return false;
+ }
+ }
+ is_ndarray = true;
+ return sax->end_array();
+ }
+ result = 0;
+ return true;
+ }
+
+ default:
+ break;
+ }
+ auto last_token = get_token_string();
+ std::string message;
+
+ if (input_format != input_format_t::bjdata)
+ {
+ message = "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token;
+ }
+ else
+ {
+ message = "expected length type specification (U, i, u, I, m, l, M, L) after '#'; last byte: 0x" + last_token;
+ }
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format, message, "size"), nullptr));
+ }
+
+ /*!
+ @brief determine the type and size for a container
+
+ In the optimized UBJSON format, a type and a size can be provided to allow
+ for a more compact representation.
+
+ @param[out] result pair of the size and the type
+ @param[in] inside_ndarray whether the parser is parsing an ND array dimensional vector
+
+ @return whether pair creation completed
+ */
+ bool get_ubjson_size_type(std::pair<std::size_t, char_int_type>& result, bool inside_ndarray = false)
+ {
+ result.first = npos; // size
+ result.second = 0; // type
+ bool is_ndarray = false;
+
+ get_ignore_noop();
+
+ if (current == '$')
+ {
+ result.second = get(); // must not ignore 'N', because 'N' maybe the type
+ if (input_format == input_format_t::bjdata
+ && JSON_HEDLEY_UNLIKELY(std::binary_search(bjd_optimized_type_markers.begin(), bjd_optimized_type_markers.end(), result.second)))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format, concat("marker 0x", last_token, " is not a permitted optimized array type"), "type"), nullptr));
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "type")))
+ {
+ return false;
+ }
+
+ get_ignore_noop();
+ if (JSON_HEDLEY_UNLIKELY(current != '#'))
+ {
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "value")))
+ {
+ return false;
+ }
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format, concat("expected '#' after type information; last byte: 0x", last_token), "size"), nullptr));
+ }
+
+ const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+ if (input_format == input_format_t::bjdata && is_ndarray)
+ {
+ if (inside_ndarray)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+ exception_message(input_format, "ndarray can not be recursive", "size"), nullptr));
+ }
+ result.second |= (1 << 8); // use bit 8 to indicate ndarray, all UBJSON and BJData markers should be ASCII letters
+ }
+ return is_error;
+ }
+
+ if (current == '#')
+ {
+ const bool is_error = get_ubjson_size_value(result.first, is_ndarray);
+ if (input_format == input_format_t::bjdata && is_ndarray)
+ {
+ return sax->parse_error(chars_read, get_token_string(), parse_error::create(112, chars_read,
+ exception_message(input_format, "ndarray requires both type and size", "size"), nullptr));
+ }
+ return is_error;
+ }
+
+ return true;
+ }
+
+ /*!
+ @param prefix the previously read or set type prefix
+ @return whether value creation completed
+ */
+ bool get_ubjson_value(const char_int_type prefix)
+ {
+ switch (prefix)
+ {
+ case char_traits<char_type>::eof(): // EOF
+ return unexpect_eof(input_format, "value");
+
+ case 'T': // true
+ return sax->boolean(true);
+ case 'F': // false
+ return sax->boolean(false);
+
+ case 'Z': // null
+ return sax->null();
+
+ case 'B': // byte
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint8_t number{};
+ return get_number(input_format, number) && sax->number_unsigned(number);
+ }
+
+ case 'U':
+ {
+ std::uint8_t number{};
+ return get_number(input_format, number) && sax->number_unsigned(number);
+ }
+
+ case 'i':
+ {
+ std::int8_t number{};
+ return get_number(input_format, number) && sax->number_integer(number);
+ }
+
+ case 'I':
+ {
+ std::int16_t number{};
+ return get_number(input_format, number) && sax->number_integer(number);
+ }
+
+ case 'l':
+ {
+ std::int32_t number{};
+ return get_number(input_format, number) && sax->number_integer(number);
+ }
+
+ case 'L':
+ {
+ std::int64_t number{};
+ return get_number(input_format, number) && sax->number_integer(number);
+ }
+
+ case 'u':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint16_t number{};
+ return get_number(input_format, number) && sax->number_unsigned(number);
+ }
+
+ case 'm':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint32_t number{};
+ return get_number(input_format, number) && sax->number_unsigned(number);
+ }
+
+ case 'M':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ std::uint64_t number{};
+ return get_number(input_format, number) && sax->number_unsigned(number);
+ }
+
+ case 'h':
+ {
+ if (input_format != input_format_t::bjdata)
+ {
+ break;
+ }
+ const auto byte1_raw = get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+ {
+ return false;
+ }
+ const auto byte2_raw = get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+ {
+ return false;
+ }
+
+ const auto byte1 = static_cast<unsigned char>(byte1_raw);
+ const auto byte2 = static_cast<unsigned char>(byte2_raw);
+
+ // code from RFC 7049, Appendix D, Figure 3:
+ // As half-precision floating-point numbers were only added
+ // to IEEE 754 in 2008, today's programming platforms often
+ // still only have limited support for them. It is very
+ // easy to include at least decoding support for them even
+ // without such support. An example of a small decoder for
+ // half-precision floating-point numbers in the C language
+ // is shown in Fig. 3.
+ const auto half = static_cast<unsigned int>((byte2 << 8u) + byte1);
+ const double val = [&half]
+ {
+ const int exp = (half >> 10u) & 0x1Fu;
+ const unsigned int mant = half & 0x3FFu;
+ JSON_ASSERT(0 <= exp&& exp <= 32);
+ JSON_ASSERT(mant <= 1024);
+ switch (exp)
+ {
+ case 0:
+ return std::ldexp(mant, -24);
+ case 31:
+ return (mant == 0)
+ ? std::numeric_limits<double>::infinity()
+ : std::numeric_limits<double>::quiet_NaN();
+ default:
+ return std::ldexp(mant + 1024, exp - 25);
+ }
+ }();
+ return sax->number_float((half & 0x8000u) != 0
+ ? static_cast<number_float_t>(-val)
+ : static_cast<number_float_t>(val), "");
+ }
+
+ case 'd':
+ {
+ float number{};
+ return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 'D':
+ {
+ double number{};
+ return get_number(input_format, number) && sax->number_float(static_cast<number_float_t>(number), "");
+ }
+
+ case 'H':
+ {
+ return get_ubjson_high_precision_number();
+ }
+
+ case 'C': // char
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "char")))
+ {
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(current > 127))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read,
+ exception_message(input_format, concat("byte after 'C' must be in range 0x00..0x7F; last byte: 0x", last_token), "char"), nullptr));
+ }
+ string_t s(1, static_cast<typename string_t::value_type>(current));
+ return sax->string(s);
+ }
+
+ case 'S': // string
+ {
+ string_t s;
+ return get_ubjson_string(s) && sax->string(s);
+ }
+
+ case '[': // array
+ return get_ubjson_array();
+
+ case '{': // object
+ return get_ubjson_object();
+
+ default: // anything else
+ break;
+ }
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format, "invalid byte: 0x" + last_token, "value"), nullptr));
+ }
+
+ /*!
+ @return whether array creation completed
+ */
+ bool get_ubjson_array()
+ {
+ std::pair<std::size_t, char_int_type> size_and_type;
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+ {
+ return false;
+ }
+
+ // if bit-8 of size_and_type.second is set to 1, encode bjdata ndarray as an object in JData annotated array format (https://github.com/NeuroJSON/jdata):
+ // {"_ArrayType_" : "typeid", "_ArraySize_" : [n1, n2, ...], "_ArrayData_" : [v1, v2, ...]}
+
+ if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
+ {
+ size_and_type.second &= ~(static_cast<char_int_type>(1) << 8); // use bit 8 to indicate ndarray, here we remove the bit to restore the type marker
+ auto it = std::lower_bound(bjd_types_map.begin(), bjd_types_map.end(), size_and_type.second, [](const bjd_type & p, char_int_type t)
+ {
+ return p.first < t;
+ });
+ string_t key = "_ArrayType_";
+ if (JSON_HEDLEY_UNLIKELY(it == bjd_types_map.end() || it->first != size_and_type.second))
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format, "invalid byte: 0x" + last_token, "type"), nullptr));
+ }
+
+ string_t type = it->second; // sax->string() takes a reference
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->string(type)))
+ {
+ return false;
+ }
+
+ if (size_and_type.second == 'C' || size_and_type.second == 'B')
+ {
+ size_and_type.second = 'U';
+ }
+
+ key = "_ArrayData_";
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(key) || !sax->start_array(size_and_type.first) ))
+ {
+ return false;
+ }
+
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+ {
+ return false;
+ }
+ }
+
+ return (sax->end_array() && sax->end_object());
+ }
+
+ // If BJData type marker is 'B' decode as binary
+ if (input_format == input_format_t::bjdata && size_and_type.first != npos && size_and_type.second == 'B')
+ {
+ binary_t result;
+ return get_binary(input_format, size_and_type.first, result) && sax->binary(result);
+ }
+
+ if (size_and_type.first != npos)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first)))
+ {
+ return false;
+ }
+
+ if (size_and_type.second != 0)
+ {
+ if (size_and_type.second != 'N')
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
+ {
+ return false;
+ }
+
+ while (current != ']')
+ {
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false)))
+ {
+ return false;
+ }
+ get_ignore_noop();
+ }
+ }
+
+ return sax->end_array();
+ }
+
+ /*!
+ @return whether object creation completed
+ */
+ bool get_ubjson_object()
+ {
+ std::pair<std::size_t, char_int_type> size_and_type;
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type)))
+ {
+ return false;
+ }
+
+ // do not accept ND-array size in objects in BJData
+ if (input_format == input_format_t::bjdata && size_and_type.first != npos && (size_and_type.second & (1 << 8)) != 0)
+ {
+ auto last_token = get_token_string();
+ return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read,
+ exception_message(input_format, "BJData object does not support ND-array size in optimized format", "object"), nullptr));
+ }
+
+ string_t key;
+ if (size_and_type.first != npos)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first)))
+ {
+ return false;
+ }
+
+ if (size_and_type.second != 0)
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second)))
+ {
+ return false;
+ }
+ key.clear();
+ }
+ }
+ else
+ {
+ for (std::size_t i = 0; i < size_and_type.first; ++i)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key)))
+ {
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+ {
+ return false;
+ }
+ key.clear();
+ }
+ }
+ }
+ else
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
+ {
+ return false;
+ }
+
+ while (current != '}')
+ {
+ if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key)))
+ {
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal()))
+ {
+ return false;
+ }
+ get_ignore_noop();
+ key.clear();
+ }
+ }
+
+ return sax->end_object();
+ }
+
+ // Note, no reader for UBJSON binary types is implemented because they do
+ // not exist
+
+ bool get_ubjson_high_precision_number()
+ {
+ // get size of following number string
+ std::size_t size{};
+ bool no_ndarray = true;
+ auto res = get_ubjson_size_value(size, no_ndarray);
+ if (JSON_HEDLEY_UNLIKELY(!res))
+ {
+ return res;
+ }
+
+ // get number string
+ std::vector<char> number_vector;
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format, "number")))
+ {
+ return false;
+ }
+ number_vector.push_back(static_cast<char>(current));
+ }
+
+ // parse number string
+ using ia_type = decltype(detail::input_adapter(number_vector));
+ auto number_lexer = detail::lexer<BasicJsonType, ia_type>(detail::input_adapter(number_vector), false);
+ const auto result_number = number_lexer.scan();
+ const auto number_string = number_lexer.get_token_string();
+ const auto result_remainder = number_lexer.scan();
+
+ using token_type = typename detail::lexer_base<BasicJsonType>::token_type;
+
+ if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
+ {
+ return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+ exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+ }
+
+ switch (result_number)
+ {
+ case token_type::value_integer:
+ return sax->number_integer(number_lexer.get_number_integer());
+ case token_type::value_unsigned:
+ return sax->number_unsigned(number_lexer.get_number_unsigned());
+ case token_type::value_float:
+ return sax->number_float(number_lexer.get_number_float(), std::move(number_string));
+ case token_type::uninitialized:
+ case token_type::literal_true:
+ case token_type::literal_false:
+ case token_type::literal_null:
+ case token_type::value_string:
+ case token_type::begin_array:
+ case token_type::begin_object:
+ case token_type::end_array:
+ case token_type::end_object:
+ case token_type::name_separator:
+ case token_type::value_separator:
+ case token_type::parse_error:
+ case token_type::end_of_input:
+ case token_type::literal_or_value:
+ default:
+ return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read,
+ exception_message(input_format, concat("invalid number text: ", number_lexer.get_token_string()), "high-precision number"), nullptr));
+ }
+ }
+
+ ///////////////////////
+ // Utility functions //
+ ///////////////////////
+
+ /*!
+ @brief get next character from the input
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns a -'ve valued
+ `char_traits<char_type>::eof()` in that case.
+
+ @return character read from the input
+ */
+ char_int_type get()
+ {
+ ++chars_read;
+ return current = ia.get_character();
+ }
+
+ /*!
+ @brief get_to read into a primitive type
+
+ This function provides the interface to the used input adapter. It does
+ not throw in case the input reached EOF, but returns false instead
+
+ @return bool, whether the read was successful
+ */
+ template<class T>
+ bool get_to(T& dest, const input_format_t format, const char* context)
+ {
+ auto new_chars_read = ia.get_elements(&dest);
+ chars_read += new_chars_read;
+ if (JSON_HEDLEY_UNLIKELY(new_chars_read < sizeof(T)))
+ {
+ // in case of failure, advance position by 1 to report failing location
+ ++chars_read;
+ sax->parse_error(chars_read, "<end of file>", parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr));
+ return false;
+ }
+ return true;
+ }
+
+ /*!
+ @return character read from the input after ignoring all 'N' entries
+ */
+ char_int_type get_ignore_noop()
+ {
+ do
+ {
+ get();
+ }
+ while (current == 'N');
+
+ return current;
+ }
+
+ template<class NumberType>
+ static void byte_swap(NumberType& number)
+ {
+ constexpr std::size_t sz = sizeof(number);
+#ifdef __cpp_lib_byteswap
+ if constexpr (sz == 1)
+ {
+ return;
+ }
+ if constexpr(std::is_integral_v<NumberType>)
+ {
+ number = std::byteswap(number);
+ return;
+ }
+#endif
+ auto* ptr = reinterpret_cast<std::uint8_t*>(&number);
+ for (std::size_t i = 0; i < sz / 2; ++i)
+ {
+ std::swap(ptr[i], ptr[sz - i - 1]);
+ }
+ }
+
+ /*
+ @brief read a number from the input
+
+ @tparam NumberType the type of the number
+ @param[in] format the current format (for diagnostics)
+ @param[out] result number of type @a NumberType
+
+ @return whether conversion completed
+
+ @note This function needs to respect the system's endianness, because
+ bytes in CBOR, MessagePack, and UBJSON are stored in network order
+ (big endian) and therefore need reordering on little endian systems.
+ On the other hand, BSON and BJData use little endian and should reorder
+ on big endian systems.
+ */
+ template<typename NumberType, bool InputIsLittleEndian = false>
+ bool get_number(const input_format_t format, NumberType& result)
+ {
+ // read in the original format
+
+ if (JSON_HEDLEY_UNLIKELY(!get_to(result, format, "number")))
+ {
+ return false;
+ }
+ if (is_little_endian != (InputIsLittleEndian || format == input_format_t::bjdata))
+ {
+ byte_swap(result);
+ }
+ return true;
+ }
+
+ /*!
+ @brief create a string by reading characters from the input
+
+ @tparam NumberType the type of the number
+ @param[in] format the current format (for diagnostics)
+ @param[in] len number of characters to read
+ @param[out] result string created by reading @a len bytes
+
+ @return whether string creation completed
+
+ @note We can not reserve @a len bytes for the result, because @a len
+ may be too large. Usually, @ref unexpect_eof() detects the end of
+ the input before we run out of string memory.
+ */
+ template<typename NumberType>
+ bool get_string(const input_format_t format,
+ const NumberType len,
+ string_t& result)
+ {
+ bool success = true;
+ for (NumberType i = 0; i < len; i++)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string")))
+ {
+ success = false;
+ break;
+ }
+ result.push_back(static_cast<typename string_t::value_type>(current));
+ }
+ return success;
+ }
+
+ /*!
+ @brief create a byte array by reading bytes from the input
+
+ @tparam NumberType the type of the number
+ @param[in] format the current format (for diagnostics)
+ @param[in] len number of bytes to read
+ @param[out] result byte array created by reading @a len bytes
+
+ @return whether byte array creation completed
+
+ @note We can not reserve @a len bytes for the result, because @a len
+ may be too large. Usually, @ref unexpect_eof() detects the end of
+ the input before we run out of memory.
+ */
+ template<typename NumberType>
+ bool get_binary(const input_format_t format,
+ const NumberType len,
+ binary_t& result)
+ {
+ bool success = true;
+ for (NumberType i = 0; i < len; i++)
+ {
+ get();
+ if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary")))
+ {
+ success = false;
+ break;
+ }
+ result.push_back(static_cast<std::uint8_t>(current));
+ }
+ return success;
+ }
+
+ /*!
+ @param[in] format the current format (for diagnostics)
+ @param[in] context further context information (for diagnostics)
+ @return whether the last read character is not EOF
+ */
+ JSON_HEDLEY_NON_NULL(3)
+ bool unexpect_eof(const input_format_t format, const char* context) const
+ {
+ if (JSON_HEDLEY_UNLIKELY(current == char_traits<char_type>::eof()))
+ {
+ return sax->parse_error(chars_read, "<end of file>",
+ parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), nullptr));
+ }
+ return true;
+ }
+
+ /*!
+ @return a string representation of the last read byte
+ */
+ std::string get_token_string() const
+ {
+ std::array<char, 3> cr{{}};
+ static_cast<void>((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ return std::string{cr.data()};
+ }
+
+ /*!
+ @param[in] format the current format
+ @param[in] detail a detailed error message
+ @param[in] context further context information
+ @return a message string to use in the parse_error exceptions
+ */
+ std::string exception_message(const input_format_t format,
+ const std::string& detail,
+ const std::string& context) const
+ {
+ std::string error_msg = "syntax error while parsing ";
+
+ switch (format)
+ {
+ case input_format_t::cbor:
+ error_msg += "CBOR";
+ break;
+
+ case input_format_t::msgpack:
+ error_msg += "MessagePack";
+ break;
+
+ case input_format_t::ubjson:
+ error_msg += "UBJSON";
+ break;
+
+ case input_format_t::bson:
+ error_msg += "BSON";
+ break;
+
+ case input_format_t::bjdata:
+ error_msg += "BJData";
+ break;
+
+ case input_format_t::json: // LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+
+ return concat(error_msg, ' ', context, ": ", detail);
+ }
+
+ private:
+ static JSON_INLINE_VARIABLE constexpr std::size_t npos = detail::unknown_size();
+
+ /// input adapter
+ InputAdapterType ia;
+
+ /// the current character
+ char_int_type current = char_traits<char_type>::eof();
+
+ /// the number of characters read
+ std::size_t chars_read = 0;
+
+ /// whether we can assume little endianness
+ const bool is_little_endian = little_endianness();
+
+ /// input format
+ const input_format_t input_format = input_format_t::json;
+
+ /// the SAX parser
+ json_sax_t* sax = nullptr;
+
+ // excluded markers in bjdata optimized type
+#define JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_ \
+ make_array<char_int_type>('F', 'H', 'N', 'S', 'T', 'Z', '[', '{')
+
+#define JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_ \
+ make_array<bjd_type>( \
+ bjd_type{'B', "byte"}, \
+ bjd_type{'C', "char"}, \
+ bjd_type{'D', "double"}, \
+ bjd_type{'I', "int16"}, \
+ bjd_type{'L', "int64"}, \
+ bjd_type{'M', "uint64"}, \
+ bjd_type{'U', "uint8"}, \
+ bjd_type{'d', "single"}, \
+ bjd_type{'i', "int8"}, \
+ bjd_type{'l', "int32"}, \
+ bjd_type{'m', "uint32"}, \
+ bjd_type{'u', "uint16"})
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ // lookup tables
+ // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+ const decltype(JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_) bjd_optimized_type_markers =
+ JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_;
+
+ using bjd_type = std::pair<char_int_type, string_t>;
+ // NOLINTNEXTLINE(cppcoreguidelines-non-private-member-variables-in-classes)
+ const decltype(JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_) bjd_types_map =
+ JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_;
+
+#undef JSON_BINARY_READER_MAKE_BJD_OPTIMIZED_TYPE_MARKERS_
+#undef JSON_BINARY_READER_MAKE_BJD_TYPES_MAP_
+};
+
+#ifndef JSON_HAS_CPP_17
+ template<typename BasicJsonType, typename InputAdapterType, typename SAX>
+ constexpr std::size_t binary_reader<BasicJsonType, InputAdapterType, SAX>::npos;
+#endif
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/input/parser.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cmath> // isfinite
+#include <cstdint> // uint8_t
+#include <functional> // function
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/input/input_adapters.hpp>
+
+// #include <nlohmann/detail/input/json_sax.hpp>
+
+// #include <nlohmann/detail/input/lexer.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/is_sax.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+////////////
+// parser //
+////////////
+
+enum class parse_event_t : std::uint8_t
+{
+ /// the parser read `{` and started to process a JSON object
+ object_start,
+ /// the parser read `}` and finished processing a JSON object
+ object_end,
+ /// the parser read `[` and started to process a JSON array
+ array_start,
+ /// the parser read `]` and finished processing a JSON array
+ array_end,
+ /// the parser read a key of a value in an object
+ key,
+ /// the parser finished reading a JSON value
+ value
+};
+
+template<typename BasicJsonType>
+using parser_callback_t =
+ std::function<bool(int /*depth*/, parse_event_t /*event*/, BasicJsonType& /*parsed*/)>;
+
+/*!
+@brief syntax analysis
+
+This class implements a recursive descent parser.
+*/
+template<typename BasicJsonType, typename InputAdapterType>
+class parser
+{
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using string_t = typename BasicJsonType::string_t;
+ using lexer_t = lexer<BasicJsonType, InputAdapterType>;
+ using token_type = typename lexer_t::token_type;
+
+ public:
+ /// a parser reading from an input adapter
+ explicit parser(InputAdapterType&& adapter,
+ parser_callback_t<BasicJsonType> cb = nullptr,
+ const bool allow_exceptions_ = true,
+ const bool skip_comments = false)
+ : callback(std::move(cb))
+ , m_lexer(std::move(adapter), skip_comments)
+ , allow_exceptions(allow_exceptions_)
+ {
+ // read first token
+ get_token();
+ }
+
+ /*!
+ @brief public parser interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @param[in,out] result parsed JSON value
+
+ @throw parse_error.101 in case of an unexpected token
+ @throw parse_error.102 if to_unicode fails or surrogate error
+ @throw parse_error.103 if to_unicode fails
+ */
+ void parse(const bool strict, BasicJsonType& result)
+ {
+ if (callback)
+ {
+ json_sax_dom_callback_parser<BasicJsonType, InputAdapterType> sdp(result, callback, allow_exceptions, &m_lexer);
+ sax_parse_internal(&sdp);
+
+ // in strict mode, input must be completely read
+ if (strict && (get_token() != token_type::end_of_input))
+ {
+ sdp.parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(),
+ exception_message(token_type::end_of_input, "value"), nullptr));
+ }
+
+ // in case of an error, return discarded value
+ if (sdp.is_errored())
+ {
+ result = value_t::discarded;
+ return;
+ }
+
+ // set top-level value to null if it was discarded by the callback
+ // function
+ if (result.is_discarded())
+ {
+ result = nullptr;
+ }
+ }
+ else
+ {
+ json_sax_dom_parser<BasicJsonType, InputAdapterType> sdp(result, allow_exceptions, &m_lexer);
+ sax_parse_internal(&sdp);
+
+ // in strict mode, input must be completely read
+ if (strict && (get_token() != token_type::end_of_input))
+ {
+ sdp.parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+ }
+
+ // in case of an error, return discarded value
+ if (sdp.is_errored())
+ {
+ result = value_t::discarded;
+ return;
+ }
+ }
+
+ result.assert_invariant();
+ }
+
+ /*!
+ @brief public accept interface
+
+ @param[in] strict whether to expect the last token to be EOF
+ @return whether the input is a proper JSON text
+ */
+ bool accept(const bool strict = true)
+ {
+ json_sax_acceptor<BasicJsonType> sax_acceptor;
+ return sax_parse(&sax_acceptor, strict);
+ }
+
+ template<typename SAX>
+ JSON_HEDLEY_NON_NULL(2)
+ bool sax_parse(SAX* sax, const bool strict = true)
+ {
+ (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
+ const bool result = sax_parse_internal(sax);
+
+ // strict mode: next byte must be EOF
+ if (result && strict && (get_token() != token_type::end_of_input))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), nullptr));
+ }
+
+ return result;
+ }
+
+ private:
+ template<typename SAX>
+ JSON_HEDLEY_NON_NULL(2)
+ bool sax_parse_internal(SAX* sax)
+ {
+ // stack to remember the hierarchy of structured values we are parsing
+ // true = array; false = object
+ std::vector<bool> states;
+ // value to avoid a goto (see comment where set to true)
+ bool skip_to_state_evaluation = false;
+
+ while (true)
+ {
+ if (!skip_to_state_evaluation)
+ {
+ // invariant: get_token() was called before each iteration
+ switch (last_token)
+ {
+ case token_type::begin_object:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_object(detail::unknown_size())))
+ {
+ return false;
+ }
+
+ // closing } -> we are done
+ if (get_token() == token_type::end_object)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+ {
+ return false;
+ }
+ break;
+ }
+
+ // parse key
+ if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+ }
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+ {
+ return false;
+ }
+
+ // parse separator (:)
+ if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+ }
+
+ // remember we are now inside an object
+ states.push_back(false);
+
+ // parse values
+ get_token();
+ continue;
+ }
+
+ case token_type::begin_array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->start_array(detail::unknown_size())))
+ {
+ return false;
+ }
+
+ // closing ] -> we are done
+ if (get_token() == token_type::end_array)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+ {
+ return false;
+ }
+ break;
+ }
+
+ // remember we are now inside an array
+ states.push_back(true);
+
+ // parse values (no need to call get_token)
+ continue;
+ }
+
+ case token_type::value_float:
+ {
+ const auto res = m_lexer.get_number_float();
+
+ if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res)))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ out_of_range::create(406, concat("number overflow parsing '", m_lexer.get_token_string(), '\''), nullptr));
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
+ {
+ return false;
+ }
+
+ break;
+ }
+
+ case token_type::literal_false:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false)))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::literal_null:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->null()))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::literal_true:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true)))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::value_integer:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer())))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::value_string:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string())))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::value_unsigned:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned())))
+ {
+ return false;
+ }
+ break;
+ }
+
+ case token_type::parse_error:
+ {
+ // using "uninitialized" to avoid "expected" message
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), nullptr));
+ }
+ case token_type::end_of_input:
+ {
+ if (JSON_HEDLEY_UNLIKELY(m_lexer.get_position().chars_read_total == 1))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(),
+ "attempting to parse an empty input; check that your input string or stream contains the expected JSON", nullptr));
+ }
+
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
+ }
+ case token_type::uninitialized:
+ case token_type::end_array:
+ case token_type::end_object:
+ case token_type::name_separator:
+ case token_type::value_separator:
+ case token_type::literal_or_value:
+ default: // the last token was unexpected
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), nullptr));
+ }
+ }
+ }
+ else
+ {
+ skip_to_state_evaluation = false;
+ }
+
+ // we reached this line after we successfully parsed a value
+ if (states.empty())
+ {
+ // empty stack: we reached the end of the hierarchy: done
+ return true;
+ }
+
+ if (states.back()) // array
+ {
+ // comma -> next value
+ if (get_token() == token_type::value_separator)
+ {
+ // parse a new value
+ get_token();
+ continue;
+ }
+
+ // closing ]
+ if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array))
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_array()))
+ {
+ return false;
+ }
+
+ // We are done with this array. Before we can parse a
+ // new value, we need to evaluate the new state first.
+ // By setting skip_to_state_evaluation to false, we
+ // are effectively jumping to the beginning of this if.
+ JSON_ASSERT(!states.empty());
+ states.pop_back();
+ skip_to_state_evaluation = true;
+ continue;
+ }
+
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), nullptr));
+ }
+
+ // states.back() is false -> object
+
+ // comma -> next value
+ if (get_token() == token_type::value_separator)
+ {
+ // parse key
+ if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), nullptr));
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
+ {
+ return false;
+ }
+
+ // parse separator (:)
+ if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator))
+ {
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), nullptr));
+ }
+
+ // parse values
+ get_token();
+ continue;
+ }
+
+ // closing }
+ if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object))
+ {
+ if (JSON_HEDLEY_UNLIKELY(!sax->end_object()))
+ {
+ return false;
+ }
+
+ // We are done with this object. Before we can parse a
+ // new value, we need to evaluate the new state first.
+ // By setting skip_to_state_evaluation to false, we
+ // are effectively jumping to the beginning of this if.
+ JSON_ASSERT(!states.empty());
+ states.pop_back();
+ skip_to_state_evaluation = true;
+ continue;
+ }
+
+ return sax->parse_error(m_lexer.get_position(),
+ m_lexer.get_token_string(),
+ parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), nullptr));
+ }
+ }
+
+ /// get next token from lexer
+ token_type get_token()
+ {
+ return last_token = m_lexer.scan();
+ }
+
+ std::string exception_message(const token_type expected, const std::string& context)
+ {
+ std::string error_msg = "syntax error ";
+
+ if (!context.empty())
+ {
+ error_msg += concat("while parsing ", context, ' ');
+ }
+
+ error_msg += "- ";
+
+ if (last_token == token_type::parse_error)
+ {
+ error_msg += concat(m_lexer.get_error_message(), "; last read: '",
+ m_lexer.get_token_string(), '\'');
+ }
+ else
+ {
+ error_msg += concat("unexpected ", lexer_t::token_type_name(last_token));
+ }
+
+ if (expected != token_type::uninitialized)
+ {
+ error_msg += concat("; expected ", lexer_t::token_type_name(expected));
+ }
+
+ return error_msg;
+ }
+
+ private:
+ /// callback function
+ const parser_callback_t<BasicJsonType> callback = nullptr;
+ /// the type of the last read token
+ token_type last_token = token_type::uninitialized;
+ /// the lexer
+ lexer_t m_lexer;
+ /// whether to throw exceptions in case of errors
+ const bool allow_exceptions = true;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // ptrdiff_t
+#include <limits> // numeric_limits
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*
+@brief an iterator for primitive JSON types
+
+This class models an iterator for primitive JSON types (boolean, number,
+string). It's only purpose is to allow the iterator/const_iterator classes
+to "iterate" over primitive values. Internally, the iterator is modeled by
+a `difference_type` variable. Value begin_value (`0`) models the begin,
+end_value (`1`) models past the end.
+*/
+class primitive_iterator_t
+{
+ private:
+ using difference_type = std::ptrdiff_t;
+ static constexpr difference_type begin_value = 0;
+ static constexpr difference_type end_value = begin_value + 1;
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /// iterator as signed integer type
+ difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();
+
+ public:
+ constexpr difference_type get_value() const noexcept
+ {
+ return m_it;
+ }
+
+ /// set iterator to a defined beginning
+ void set_begin() noexcept
+ {
+ m_it = begin_value;
+ }
+
+ /// set iterator to a defined past the end
+ void set_end() noexcept
+ {
+ m_it = end_value;
+ }
+
+ /// return whether the iterator can be dereferenced
+ constexpr bool is_begin() const noexcept
+ {
+ return m_it == begin_value;
+ }
+
+ /// return whether the iterator is at end
+ constexpr bool is_end() const noexcept
+ {
+ return m_it == end_value;
+ }
+
+ friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it == rhs.m_it;
+ }
+
+ friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it < rhs.m_it;
+ }
+
+ primitive_iterator_t operator+(difference_type n) noexcept
+ {
+ auto result = *this;
+ result += n;
+ return result;
+ }
+
+ friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept
+ {
+ return lhs.m_it - rhs.m_it;
+ }
+
+ primitive_iterator_t& operator++() noexcept
+ {
+ ++m_it;
+ return *this;
+ }
+
+ primitive_iterator_t operator++(int)& noexcept // NOLINT(cert-dcl21-cpp)
+ {
+ auto result = *this;
+ ++m_it;
+ return result;
+ }
+
+ primitive_iterator_t& operator--() noexcept
+ {
+ --m_it;
+ return *this;
+ }
+
+ primitive_iterator_t operator--(int)& noexcept // NOLINT(cert-dcl21-cpp)
+ {
+ auto result = *this;
+ --m_it;
+ return result;
+ }
+
+ primitive_iterator_t& operator+=(difference_type n) noexcept
+ {
+ m_it += n;
+ return *this;
+ }
+
+ primitive_iterator_t& operator-=(difference_type n) noexcept
+ {
+ m_it -= n;
+ return *this;
+ }
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief an iterator value
+
+@note This structure could easily be a union, but MSVC currently does not allow
+unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
+*/
+template<typename BasicJsonType> struct internal_iterator
+{
+ /// iterator for JSON objects
+ typename BasicJsonType::object_t::iterator object_iterator {};
+ /// iterator for JSON arrays
+ typename BasicJsonType::array_t::iterator array_iterator {};
+ /// generic iterator for all other types
+ primitive_iterator_t primitive_iterator {};
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/iter_impl.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next
+#include <type_traits> // conditional, is_const, remove_const
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/iterators/internal_iterator.hpp>
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+// forward declare, to be able to friend it later on
+template<typename IteratorType> class iteration_proxy;
+template<typename IteratorType> class iteration_proxy_value;
+
+/*!
+@brief a template for a bidirectional iterator for the @ref basic_json class
+This class implements a both iterators (iterator and const_iterator) for the
+@ref basic_json class.
+@note An iterator is called *initialized* when a pointer to a JSON value has
+ been set (e.g., by a constructor or a copy assignment). If the iterator is
+ default-constructed, it is *uninitialized* and most methods are undefined.
+ **The library uses assertions to detect calls on uninitialized iterators.**
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+ The iterator that can be moved can be moved in both directions (i.e.
+ incremented and decremented).
+@since version 1.0.0, simplified in version 2.0.9, change to bidirectional
+ iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)
+*/
+template<typename BasicJsonType>
+class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
+{
+ /// the iterator with BasicJsonType of different const-ness
+ using other_iter_impl = iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;
+ /// allow basic_json to access private members
+ friend other_iter_impl;
+ friend BasicJsonType;
+ friend iteration_proxy<iter_impl>;
+ friend iteration_proxy_value<iter_impl>;
+
+ using object_t = typename BasicJsonType::object_t;
+ using array_t = typename BasicJsonType::array_t;
+ // make sure BasicJsonType is basic_json or const basic_json
+ static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,
+ "iter_impl only accepts (const) basic_json");
+ // superficial check for the LegacyBidirectionalIterator named requirement
+ static_assert(std::is_base_of<std::bidirectional_iterator_tag, std::bidirectional_iterator_tag>::value
+ && std::is_base_of<std::bidirectional_iterator_tag, typename std::iterator_traits<typename array_t::iterator>::iterator_category>::value,
+ "basic_json iterator assumes array and object type iterators satisfy the LegacyBidirectionalIterator named requirement.");
+
+ public:
+ /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.
+ /// The C++ Standard has never required user-defined iterators to derive from std::iterator.
+ /// A user-defined iterator should provide publicly accessible typedefs named
+ /// iterator_category, value_type, difference_type, pointer, and reference.
+ /// Note that value_type is required to be non-const, even for constant iterators.
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ /// the type of the values when the iterator is dereferenced
+ using value_type = typename BasicJsonType::value_type;
+ /// a type to represent differences between iterators
+ using difference_type = typename BasicJsonType::difference_type;
+ /// defines a pointer to the type iterated over (value_type)
+ using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,
+ typename BasicJsonType::const_pointer,
+ typename BasicJsonType::pointer>::type;
+ /// defines a reference to the type iterated over (value_type)
+ using reference =
+ typename std::conditional<std::is_const<BasicJsonType>::value,
+ typename BasicJsonType::const_reference,
+ typename BasicJsonType::reference>::type;
+
+ iter_impl() = default;
+ ~iter_impl() = default;
+ iter_impl(iter_impl&&) noexcept = default;
+ iter_impl& operator=(iter_impl&&) noexcept = default;
+
+ /*!
+ @brief constructor for a given JSON instance
+ @param[in] object pointer to a JSON object for this iterator
+ @pre object != nullptr
+ @post The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ explicit iter_impl(pointer object) noexcept : m_object(object)
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = typename object_t::iterator();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = typename array_t::iterator();
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ m_it.primitive_iterator = primitive_iterator_t();
+ break;
+ }
+ }
+ }
+
+ /*!
+ @note The conventional copy constructor and copy assignment are implicitly
+ defined. Combined with the following converting constructor and
+ assignment, they support: (1) copy from iterator to iterator, (2)
+ copy from const iterator to const iterator, and (3) conversion from
+ iterator to const iterator. However conversion from const iterator
+ to iterator is not defined.
+ */
+
+ /*!
+ @brief const copy constructor
+ @param[in] other const iterator to copy from
+ @note This copy constructor had to be defined explicitly to circumvent a bug
+ occurring on msvc v19.0 compiler (VS 2015) debug build. For more
+ information refer to: https://github.com/nlohmann/json/issues/1608
+ */
+ iter_impl(const iter_impl<const BasicJsonType>& other) noexcept
+ : m_object(other.m_object), m_it(other.m_it)
+ {}
+
+ /*!
+ @brief converting assignment
+ @param[in] other const iterator to copy from
+ @return const/non-const iterator
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl& operator=(const iter_impl<const BasicJsonType>& other) noexcept
+ {
+ if (&other != this)
+ {
+ m_object = other.m_object;
+ m_it = other.m_it;
+ }
+ return *this;
+ }
+
+ /*!
+ @brief converting constructor
+ @param[in] other non-const iterator to copy from
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept
+ : m_object(other.m_object), m_it(other.m_it)
+ {}
+
+ /*!
+ @brief converting assignment
+ @param[in] other non-const iterator to copy from
+ @return const/non-const iterator
+ @note It is not checked whether @a other is initialized.
+ */
+ iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept // NOLINT(cert-oop54-cpp)
+ {
+ m_object = other.m_object;
+ m_it = other.m_it;
+ return *this;
+ }
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /*!
+ @brief set the iterator to the first value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ void set_begin() noexcept
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = m_object->m_data.m_value.object->begin();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = m_object->m_data.m_value.array->begin();
+ break;
+ }
+
+ case value_t::null:
+ {
+ // set to end so begin()==end() is true: null is empty
+ m_it.primitive_iterator.set_end();
+ break;
+ }
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ m_it.primitive_iterator.set_begin();
+ break;
+ }
+ }
+ }
+
+ /*!
+ @brief set the iterator past the last value
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ void set_end() noexcept
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ {
+ m_it.object_iterator = m_object->m_data.m_value.object->end();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_it.array_iterator = m_object->m_data.m_value.array->end();
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ m_it.primitive_iterator.set_end();
+ break;
+ }
+ }
+ }
+
+ public:
+ /*!
+ @brief return a reference to the value pointed to by the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference operator*() const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ {
+ JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
+ return m_it.object_iterator->second;
+ }
+
+ case value_t::array:
+ {
+ JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
+ return *m_it.array_iterator;
+ }
+
+ case value_t::null:
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+ {
+ return *m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+ }
+ }
+ }
+
+ /*!
+ @brief dereference the iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ pointer operator->() const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ {
+ JSON_ASSERT(m_it.object_iterator != m_object->m_data.m_value.object->end());
+ return &(m_it.object_iterator->second);
+ }
+
+ case value_t::array:
+ {
+ JSON_ASSERT(m_it.array_iterator != m_object->m_data.m_value.array->end());
+ return &*m_it.array_iterator;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin()))
+ {
+ return m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+ }
+ }
+ }
+
+ /*!
+ @brief post-increment (it++)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator++(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ auto result = *this;
+ ++(*this);
+ return result;
+ }
+
+ /*!
+ @brief pre-increment (++it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator++()
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ {
+ std::advance(m_it.object_iterator, 1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, 1);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ ++m_it.primitive_iterator;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief post-decrement (it--)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator--(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ auto result = *this;
+ --(*this);
+ return result;
+ }
+
+ /*!
+ @brief pre-decrement (--it)
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator--()
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ {
+ std::advance(m_it.object_iterator, -1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, -1);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ --m_it.primitive_iterator;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief comparison: equal
+ @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.
+ */
+ template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+ bool operator==(const IterImpl& other) const
+ {
+ // if objects are not the same, the comparison is undefined
+ if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+ }
+
+ // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
+ if (m_object == nullptr)
+ {
+ return true;
+ }
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ return (m_it.object_iterator == other.m_it.object_iterator);
+
+ case value_t::array:
+ return (m_it.array_iterator == other.m_it.array_iterator);
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ return (m_it.primitive_iterator == other.m_it.primitive_iterator);
+ }
+ }
+
+ /*!
+ @brief comparison: not equal
+ @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.
+ */
+ template < typename IterImpl, detail::enable_if_t < (std::is_same<IterImpl, iter_impl>::value || std::is_same<IterImpl, other_iter_impl>::value), std::nullptr_t > = nullptr >
+ bool operator!=(const IterImpl& other) const
+ {
+ return !operator==(other);
+ }
+
+ /*!
+ @brief comparison: smaller
+ @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.
+ */
+ bool operator<(const iter_impl& other) const
+ {
+ // if objects are not the same, the comparison is undefined
+ if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", m_object));
+ }
+
+ // value-initialized forward iterators can be compared, and must compare equal to other value-initialized iterators of the same type #4493
+ if (m_object == nullptr)
+ {
+ // the iterators are both value-initialized and are to be considered equal, but this function checks for smaller, so we return false
+ return false;
+ }
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", m_object));
+
+ case value_t::array:
+ return (m_it.array_iterator < other.m_it.array_iterator);
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ return (m_it.primitive_iterator < other.m_it.primitive_iterator);
+ }
+ }
+
+ /*!
+ @brief comparison: less than or equal
+ @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.
+ */
+ bool operator<=(const iter_impl& other) const
+ {
+ return !other.operator < (*this);
+ }
+
+ /*!
+ @brief comparison: greater than
+ @pre (1) Both iterators are initialized to point to the same object, or (2) both iterators are value-initialized.
+ */
+ bool operator>(const iter_impl& other) const
+ {
+ return !operator<=(other);
+ }
+
+ /*!
+ @brief comparison: greater than or equal
+ @pre (1) The iterator is initialized; i.e. `m_object != nullptr`, or (2) both iterators are value-initialized.
+ */
+ bool operator>=(const iter_impl& other) const
+ {
+ return !operator<(other);
+ }
+
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator+=(difference_type i)
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+ case value_t::array:
+ {
+ std::advance(m_it.array_iterator, i);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ m_it.primitive_iterator += i;
+ break;
+ }
+ }
+
+ return *this;
+ }
+
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl& operator-=(difference_type i)
+ {
+ return operator+=(-i);
+ }
+
+ /*!
+ @brief add to iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator+(difference_type i) const
+ {
+ auto result = *this;
+ result += i;
+ return result;
+ }
+
+ /*!
+ @brief addition of distance and iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ friend iter_impl operator+(difference_type i, const iter_impl& it)
+ {
+ auto result = it;
+ result += i;
+ return result;
+ }
+
+ /*!
+ @brief subtract from iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ iter_impl operator-(difference_type i) const
+ {
+ auto result = *this;
+ result -= i;
+ return result;
+ }
+
+ /*!
+ @brief return difference
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ difference_type operator-(const iter_impl& other) const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", m_object));
+
+ case value_t::array:
+ return m_it.array_iterator - other.m_it.array_iterator;
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ return m_it.primitive_iterator - other.m_it.primitive_iterator;
+ }
+ }
+
+ /*!
+ @brief access to successor
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference operator[](difference_type n) const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ switch (m_object->m_data.m_type)
+ {
+ case value_t::object:
+ JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", m_object));
+
+ case value_t::array:
+ return *std::next(m_it.array_iterator, n);
+
+ case value_t::null:
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n))
+ {
+ return *m_object;
+ }
+
+ JSON_THROW(invalid_iterator::create(214, "cannot get value", m_object));
+ }
+ }
+ }
+
+ /*!
+ @brief return the key of an object iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ const typename object_t::key_type& key() const
+ {
+ JSON_ASSERT(m_object != nullptr);
+
+ if (JSON_HEDLEY_LIKELY(m_object->is_object()))
+ {
+ return m_it.object_iterator->first;
+ }
+
+ JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", m_object));
+ }
+
+ /*!
+ @brief return the value of an iterator
+ @pre The iterator is initialized; i.e. `m_object != nullptr`.
+ */
+ reference value() const
+ {
+ return operator*();
+ }
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /// associated JSON instance
+ pointer m_object = nullptr;
+ /// the actual iterator of the associated instance
+ internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it {};
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/iteration_proxy.hpp>
+
+// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <cstddef> // ptrdiff_t
+#include <iterator> // reverse_iterator
+#include <utility> // declval
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+//////////////////////
+// reverse_iterator //
+//////////////////////
+
+/*!
+@brief a template for a reverse iterator class
+
+@tparam Base the base iterator type to reverse. Valid types are @ref
+iterator (to create @ref reverse_iterator) and @ref const_iterator (to
+create @ref const_reverse_iterator).
+
+@requirement The class satisfies the following concept requirements:
+-
+[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):
+ The iterator that can be moved can be moved in both directions (i.e.
+ incremented and decremented).
+- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):
+ It is possible to write to the pointed-to element (only if @a Base is
+ @ref iterator).
+
+@since version 1.0.0
+*/
+template<typename Base>
+class json_reverse_iterator : public std::reverse_iterator<Base>
+{
+ public:
+ using difference_type = std::ptrdiff_t;
+ /// shortcut to the reverse iterator adapter
+ using base_iterator = std::reverse_iterator<Base>;
+ /// the reference type for the pointed-to element
+ using reference = typename Base::reference;
+
+ /// create reverse iterator from iterator
+ explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept
+ : base_iterator(it) {}
+
+ /// create reverse iterator from base class
+ explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}
+
+ /// post-increment (it++)
+ json_reverse_iterator operator++(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator++(1));
+ }
+
+ /// pre-increment (++it)
+ json_reverse_iterator& operator++()
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator++());
+ }
+
+ /// post-decrement (it--)
+ json_reverse_iterator operator--(int)& // NOLINT(cert-dcl21-cpp)
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator--(1));
+ }
+
+ /// pre-decrement (--it)
+ json_reverse_iterator& operator--()
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator--());
+ }
+
+ /// add to iterator
+ json_reverse_iterator& operator+=(difference_type i)
+ {
+ return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));
+ }
+
+ /// add to iterator
+ json_reverse_iterator operator+(difference_type i) const
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator+(i));
+ }
+
+ /// subtract from iterator
+ json_reverse_iterator operator-(difference_type i) const
+ {
+ return static_cast<json_reverse_iterator>(base_iterator::operator-(i));
+ }
+
+ /// return difference
+ difference_type operator-(const json_reverse_iterator& other) const
+ {
+ return base_iterator(*this) - base_iterator(other);
+ }
+
+ /// access to successor
+ reference operator[](difference_type n) const
+ {
+ return *(this->operator+(n));
+ }
+
+ /// return the key of an object iterator
+ auto key() const -> decltype(std::declval<Base>().key())
+ {
+ auto it = --this->base();
+ return it.key();
+ }
+
+ /// return the value of an iterator
+ reference value() const
+ {
+ auto it = --this->base();
+ return it.operator * ();
+ }
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/iterators/primitive_iterator.hpp>
+
+// #include <nlohmann/detail/json_custom_base_class.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <type_traits> // conditional, is_same
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief Default base class of the @ref basic_json class.
+
+So that the correct implementations of the copy / move ctors / assign operators
+of @ref basic_json do not require complex case distinctions
+(no base class / custom base class used as customization point),
+@ref basic_json always has a base class.
+By default, this class is used because it is empty and thus has no effect
+on the behavior of @ref basic_json.
+*/
+struct json_default_base {};
+
+template<class T>
+using json_base_class = typename std::conditional <
+ std::is_same<T, void>::value,
+ json_default_base,
+ T
+ >::type;
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/json_pointer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // all_of
+#include <cctype> // isdigit
+#include <cerrno> // errno, ERANGE
+#include <cstdlib> // strtoull
+#ifndef JSON_NO_IO
+ #include <iosfwd> // ostream
+#endif // JSON_NO_IO
+#include <limits> // max
+#include <numeric> // accumulate
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
+/// @sa https://json.nlohmann.me/api/json_pointer/
+template<typename RefStringType>
+class json_pointer
+{
+ // allow basic_json to access private members
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ friend class basic_json;
+
+ template<typename>
+ friend class json_pointer;
+
+ template<typename T>
+ struct string_t_helper
+ {
+ using type = T;
+ };
+
+ NLOHMANN_BASIC_JSON_TPL_DECLARATION
+ struct string_t_helper<NLOHMANN_BASIC_JSON_TPL>
+ {
+ using type = StringType;
+ };
+
+ public:
+ // for backwards compatibility accept BasicJsonType
+ using string_t = typename string_t_helper<RefStringType>::type;
+
+ /// @brief create JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/
+ explicit json_pointer(const string_t& s = "")
+ : reference_tokens(split(s))
+ {}
+
+ /// @brief return a string representation of the JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/to_string/
+ string_t to_string() const
+ {
+ return std::accumulate(reference_tokens.begin(), reference_tokens.end(),
+ string_t{},
+ [](const string_t& a, const string_t& b)
+ {
+ return detail::concat(a, '/', detail::escape(b));
+ });
+ }
+
+ /// @brief return a string representation of the JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, to_string())
+ operator string_t() const
+ {
+ return to_string();
+ }
+
+#ifndef JSON_NO_IO
+ /// @brief write string representation of the JSON pointer to stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+ friend std::ostream& operator<<(std::ostream& o, const json_pointer& ptr)
+ {
+ o << ptr.to_string();
+ return o;
+ }
+#endif
+
+ /// @brief append another JSON pointer at the end of this JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+ json_pointer& operator/=(const json_pointer& ptr)
+ {
+ reference_tokens.insert(reference_tokens.end(),
+ ptr.reference_tokens.begin(),
+ ptr.reference_tokens.end());
+ return *this;
+ }
+
+ /// @brief append an unescaped reference token at the end of this JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+ json_pointer& operator/=(string_t token)
+ {
+ push_back(std::move(token));
+ return *this;
+ }
+
+ /// @brief append an array index at the end of this JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
+ json_pointer& operator/=(std::size_t array_idx)
+ {
+ return *this /= std::to_string(array_idx);
+ }
+
+ /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+ friend json_pointer operator/(const json_pointer& lhs,
+ const json_pointer& rhs)
+ {
+ return json_pointer(lhs) /= rhs;
+ }
+
+ /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+ friend json_pointer operator/(const json_pointer& lhs, string_t token) // NOLINT(performance-unnecessary-value-param)
+ {
+ return json_pointer(lhs) /= std::move(token);
+ }
+
+ /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/
+ friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx)
+ {
+ return json_pointer(lhs) /= array_idx;
+ }
+
+ /// @brief returns the parent of this JSON pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/
+ json_pointer parent_pointer() const
+ {
+ if (empty())
+ {
+ return *this;
+ }
+
+ json_pointer res = *this;
+ res.pop_back();
+ return res;
+ }
+
+ /// @brief remove last reference token
+ /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/
+ void pop_back()
+ {
+ if (JSON_HEDLEY_UNLIKELY(empty()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+ }
+
+ reference_tokens.pop_back();
+ }
+
+ /// @brief return last reference token
+ /// @sa https://json.nlohmann.me/api/json_pointer/back/
+ const string_t& back() const
+ {
+ if (JSON_HEDLEY_UNLIKELY(empty()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+ }
+
+ return reference_tokens.back();
+ }
+
+ /// @brief append an unescaped token at the end of the reference pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+ void push_back(const string_t& token)
+ {
+ reference_tokens.push_back(token);
+ }
+
+ /// @brief append an unescaped token at the end of the reference pointer
+ /// @sa https://json.nlohmann.me/api/json_pointer/push_back/
+ void push_back(string_t&& token)
+ {
+ reference_tokens.push_back(std::move(token));
+ }
+
+ /// @brief return whether pointer points to the root document
+ /// @sa https://json.nlohmann.me/api/json_pointer/empty/
+ bool empty() const noexcept
+ {
+ return reference_tokens.empty();
+ }
+
+ private:
+ /*!
+ @param[in] s reference token to be converted into an array index
+
+ @return integer representation of @a s
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index begins not with a digit
+ @throw out_of_range.404 if string @a s could not be converted to an integer
+ @throw out_of_range.410 if an array index exceeds size_type
+ */
+ template<typename BasicJsonType>
+ static typename BasicJsonType::size_type array_index(const string_t& s)
+ {
+ using size_type = typename BasicJsonType::size_type;
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
+ {
+ JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
+ }
+
+ // error condition (cf. RFC 6901, Sect. 4)
+ if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
+ {
+ JSON_THROW(detail::parse_error::create(109, 0, detail::concat("array index '", s, "' is not a number"), nullptr));
+ }
+
+ const char* p = s.c_str();
+ char* p_end = nullptr; // NOLINT(misc-const-correctness)
+ errno = 0; // strtoull doesn't reset errno
+ const unsigned long long res = std::strtoull(p, &p_end, 10); // NOLINT(runtime/int)
+ if (p == p_end // invalid input or empty string
+ || errno == ERANGE // out of range
+ || JSON_HEDLEY_UNLIKELY(static_cast<std::size_t>(p_end - p) != s.size())) // incomplete read
+ {
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", s, "'"), nullptr));
+ }
+
+ // only triggered on special platforms (like 32bit), see also
+ // https://github.com/nlohmann/json/pull/2203
+ if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)())) // NOLINT(runtime/int)
+ {
+ JSON_THROW(detail::out_of_range::create(410, detail::concat("array index ", s, " exceeds size_type"), nullptr)); // LCOV_EXCL_LINE
+ }
+
+ return static_cast<size_type>(res);
+ }
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ json_pointer top() const
+ {
+ if (JSON_HEDLEY_UNLIKELY(empty()))
+ {
+ JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", nullptr));
+ }
+
+ json_pointer result = *this;
+ result.reference_tokens = {reference_tokens[0]};
+ return result;
+ }
+
+ private:
+ /*!
+ @brief create and return a reference to the pointed to value
+
+ @complexity Linear in the number of reference tokens.
+
+ @throw parse_error.109 if array index is not a number
+ @throw type_error.313 if value cannot be unflattened
+ */
+ template<typename BasicJsonType>
+ BasicJsonType& get_and_create(BasicJsonType& j) const
+ {
+ auto* result = &j;
+
+ // in case no reference tokens exist, return a reference to the JSON value
+ // j which will be overwritten by a primitive value
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (result->type())
+ {
+ case detail::value_t::null:
+ {
+ if (reference_token == "0")
+ {
+ // start a new array if reference token is 0
+ result = &result->operator[](0);
+ }
+ else
+ {
+ // start a new object otherwise
+ result = &result->operator[](reference_token);
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ // create an entry in the object
+ result = &result->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ // create an entry in the array
+ result = &result->operator[](array_index<BasicJsonType>(reference_token));
+ break;
+ }
+
+ /*
+ The following code is only reached if there exists a reference
+ token _and_ the current value is primitive. In this case, we have
+ an error situation, because primitive values may only occur as
+ single value; that is, with an empty list of reference tokens.
+ */
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", &j));
+ }
+ }
+
+ return *result;
+ }
+
+ /*!
+ @brief return a reference to the pointed to value
+
+ @note This version does not throw if a value is not present, but tries to
+ create nested values instead. For instance, calling this function
+ with pointer `"/this/that"` on a null value is equivalent to calling
+ `operator[]("this").operator[]("that")` on that value, effectively
+ changing the null value to an object.
+
+ @param[in] ptr a JSON value
+
+ @return reference to the JSON value pointed to by the JSON pointer
+
+ @complexity Linear in the length of the JSON pointer.
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ template<typename BasicJsonType>
+ BasicJsonType& get_unchecked(BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ // convert null values to arrays or objects before continuing
+ if (ptr->is_null())
+ {
+ // check if reference token is a number
+ const bool nums =
+ std::all_of(reference_token.begin(), reference_token.end(),
+ [](const unsigned char x)
+ {
+ return std::isdigit(x);
+ });
+
+ // change value to array for numbers or "-" or to object otherwise
+ *ptr = (nums || reference_token == "-")
+ ? detail::value_t::array
+ : detail::value_t::object;
+ }
+
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (reference_token == "-")
+ {
+ // explicitly treat "-" as index beyond the end
+ ptr = &ptr->operator[](ptr->m_data.m_value.array->size());
+ }
+ else
+ {
+ // convert array index to number; unchecked access
+ ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+ }
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+ }
+ }
+
+ return *ptr;
+ }
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ template<typename BasicJsonType>
+ BasicJsonType& get_checked(BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402, detail::concat(
+ "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
+ ") is out of range"), ptr));
+ }
+
+ // note: at performs range check
+ ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+ }
+ }
+
+ return *ptr;
+ }
+
+ /*!
+ @brief return a const reference to the pointed to value
+
+ @param[in] ptr a JSON value
+
+ @return const reference to the JSON value pointed to by the JSON
+ pointer
+
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ template<typename BasicJsonType>
+ const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ // use unchecked object access
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+ {
+ // "-" cannot be used for const access
+ JSON_THROW(detail::out_of_range::create(402, detail::concat("array index '-' (", std::to_string(ptr->m_data.m_value.array->size()), ") is out of range"), ptr));
+ }
+
+ // use unchecked array access
+ ptr = &ptr->operator[](array_index<BasicJsonType>(reference_token));
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+ }
+ }
+
+ return *ptr;
+ }
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ @throw out_of_range.402 if the array index '-' is used
+ @throw out_of_range.404 if the JSON pointer can not be resolved
+ */
+ template<typename BasicJsonType>
+ const BasicJsonType& get_checked(const BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ // note: at performs range check
+ ptr = &ptr->at(reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ JSON_THROW(detail::out_of_range::create(402, detail::concat(
+ "array index '-' (", std::to_string(ptr->m_data.m_value.array->size()),
+ ") is out of range"), ptr));
+ }
+
+ // note: at performs range check
+ ptr = &ptr->at(array_index<BasicJsonType>(reference_token));
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ JSON_THROW(detail::out_of_range::create(404, detail::concat("unresolved reference token '", reference_token, "'"), ptr));
+ }
+ }
+
+ return *ptr;
+ }
+
+ /*!
+ @throw parse_error.106 if an array index begins with '0'
+ @throw parse_error.109 if an array index was not a number
+ */
+ template<typename BasicJsonType>
+ bool contains(const BasicJsonType* ptr) const
+ {
+ for (const auto& reference_token : reference_tokens)
+ {
+ switch (ptr->type())
+ {
+ case detail::value_t::object:
+ {
+ if (!ptr->contains(reference_token))
+ {
+ // we did not find the key in the object
+ return false;
+ }
+
+ ptr = &ptr->operator[](reference_token);
+ break;
+ }
+
+ case detail::value_t::array:
+ {
+ if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
+ {
+ // "-" always fails the range check
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
+ {
+ // invalid char
+ return false;
+ }
+ if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1))
+ {
+ if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9')))
+ {
+ // first char should be between '1' and '9'
+ return false;
+ }
+ for (std::size_t i = 1; i < reference_token.size(); i++)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9')))
+ {
+ // other char should be between '0' and '9'
+ return false;
+ }
+ }
+ }
+
+ const auto idx = array_index<BasicJsonType>(reference_token);
+ if (idx >= ptr->size())
+ {
+ // index out of range
+ return false;
+ }
+
+ ptr = &ptr->operator[](idx);
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ {
+ // we do not expect primitive values if there is still a
+ // reference token to process
+ return false;
+ }
+ }
+ }
+
+ // no reference token left means we found a primitive value
+ return true;
+ }
+
+ /*!
+ @brief split the string input to reference tokens
+
+ @note This function is only called by the json_pointer constructor.
+ All exceptions below are documented there.
+
+ @throw parse_error.107 if the pointer is not empty or begins with '/'
+ @throw parse_error.108 if character '~' is not followed by '0' or '1'
+ */
+ static std::vector<string_t> split(const string_t& reference_string)
+ {
+ std::vector<string_t> result;
+
+ // special case: empty reference string -> no reference tokens
+ if (reference_string.empty())
+ {
+ return result;
+ }
+
+ // check if nonempty reference string begins with slash
+ if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
+ {
+ JSON_THROW(detail::parse_error::create(107, 1, detail::concat("JSON pointer must be empty or begin with '/' - was: '", reference_string, "'"), nullptr));
+ }
+
+ // extract the reference tokens:
+ // - slash: position of the last read slash (or end of string)
+ // - start: position after the previous slash
+ for (
+ // search for the first slash after the first character
+ std::size_t slash = reference_string.find_first_of('/', 1),
+ // set the beginning of the first reference token
+ start = 1;
+ // we can stop if start == 0 (if slash == string_t::npos)
+ start != 0;
+ // set the beginning of the next reference token
+ // (will eventually be 0 if slash == string_t::npos)
+ start = (slash == string_t::npos) ? 0 : slash + 1,
+ // find next slash
+ slash = reference_string.find_first_of('/', start))
+ {
+ // use the text between the beginning of the reference token
+ // (start) and the last slash (slash).
+ auto reference_token = reference_string.substr(start, slash - start);
+
+ // check reference tokens are properly escaped
+ for (std::size_t pos = reference_token.find_first_of('~');
+ pos != string_t::npos;
+ pos = reference_token.find_first_of('~', pos + 1))
+ {
+ JSON_ASSERT(reference_token[pos] == '~');
+
+ // ~ must be followed by 0 or 1
+ if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 ||
+ (reference_token[pos + 1] != '0' &&
+ reference_token[pos + 1] != '1')))
+ {
+ JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", nullptr));
+ }
+ }
+
+ // finally, store the reference token
+ detail::unescape(reference_token);
+ result.push_back(reference_token);
+ }
+
+ return result;
+ }
+
+ private:
+ /*!
+ @param[in] reference_string the reference string to the current value
+ @param[in] value the value to consider
+ @param[in,out] result the result object to insert values to
+
+ @note Empty objects or arrays are flattened to `null`.
+ */
+ template<typename BasicJsonType>
+ static void flatten(const string_t& reference_string,
+ const BasicJsonType& value,
+ BasicJsonType& result)
+ {
+ switch (value.type())
+ {
+ case detail::value_t::array:
+ {
+ if (value.m_data.m_value.array->empty())
+ {
+ // flatten empty array as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate array and use index as reference string
+ for (std::size_t i = 0; i < value.m_data.m_value.array->size(); ++i)
+ {
+ flatten(detail::concat<string_t>(reference_string, '/', std::to_string(i)),
+ value.m_data.m_value.array->operator[](i), result);
+ }
+ }
+ break;
+ }
+
+ case detail::value_t::object:
+ {
+ if (value.m_data.m_value.object->empty())
+ {
+ // flatten empty object as null
+ result[reference_string] = nullptr;
+ }
+ else
+ {
+ // iterate object and use keys as reference string
+ for (const auto& element : *value.m_data.m_value.object)
+ {
+ flatten(detail::concat<string_t>(reference_string, '/', detail::escape(element.first)), element.second, result);
+ }
+ }
+ break;
+ }
+
+ case detail::value_t::null:
+ case detail::value_t::string:
+ case detail::value_t::boolean:
+ case detail::value_t::number_integer:
+ case detail::value_t::number_unsigned:
+ case detail::value_t::number_float:
+ case detail::value_t::binary:
+ case detail::value_t::discarded:
+ default:
+ {
+ // add primitive value with its reference string
+ result[reference_string] = value;
+ break;
+ }
+ }
+ }
+
+ /*!
+ @param[in] value flattened JSON
+
+ @return unflattened JSON
+
+ @throw parse_error.109 if array index is not a number
+ @throw type_error.314 if value is not an object
+ @throw type_error.315 if object values are not primitive
+ @throw type_error.313 if value cannot be unflattened
+ */
+ template<typename BasicJsonType>
+ static BasicJsonType
+ unflatten(const BasicJsonType& value)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
+ {
+ JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", &value));
+ }
+
+ BasicJsonType result;
+
+ // iterate the JSON object values
+ for (const auto& element : *value.m_data.m_value.object)
+ {
+ if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
+ {
+ JSON_THROW(detail::type_error::create(315, "values in object must be primitive", &element.second));
+ }
+
+ // assign value to reference pointed to by JSON pointer; Note that if
+ // the JSON pointer is "" (i.e., points to the whole value), function
+ // get_and_create returns a reference to result itself. An assignment
+ // will then create a primitive value.
+ json_pointer(element.first).get_and_create(result) = element.second;
+ }
+
+ return result;
+ }
+
+ // can't use conversion operator because of ambiguity
+ json_pointer<string_t> convert() const&
+ {
+ json_pointer<string_t> result;
+ result.reference_tokens = reference_tokens;
+ return result;
+ }
+
+ json_pointer<string_t> convert()&&
+ {
+ json_pointer<string_t> result;
+ result.reference_tokens = std::move(reference_tokens);
+ return result;
+ }
+
+ public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+ /// @brief compares two JSON pointers for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ template<typename RefStringTypeRhs>
+ bool operator==(const json_pointer<RefStringTypeRhs>& rhs) const noexcept
+ {
+ return reference_tokens == rhs.reference_tokens;
+ }
+
+ /// @brief compares JSON pointer and string for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer))
+ bool operator==(const string_t& rhs) const
+ {
+ return *this == json_pointer(rhs);
+ }
+
+ /// @brief 3-way compares two JSON pointers
+ template<typename RefStringTypeRhs>
+ std::strong_ordering operator<=>(const json_pointer<RefStringTypeRhs>& rhs) const noexcept // *NOPAD*
+ {
+ return reference_tokens <=> rhs.reference_tokens; // *NOPAD*
+ }
+#else
+ /// @brief compares two JSON pointers for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+ /// @brief compares JSON pointer and string for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ template<typename RefStringTypeLhs, typename StringType>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+ const StringType& rhs);
+
+ /// @brief compares string and JSON pointer for equality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_eq/
+ template<typename RefStringTypeRhs, typename StringType>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator==(const StringType& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs);
+
+ /// @brief compares two JSON pointers for inequality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+ template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+
+ /// @brief compares JSON pointer and string for inequality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+ template<typename RefStringTypeLhs, typename StringType>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+ const StringType& rhs);
+
+ /// @brief compares string and JSON pointer for inequality
+ /// @sa https://json.nlohmann.me/api/json_pointer/operator_ne/
+ template<typename RefStringTypeRhs, typename StringType>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator!=(const StringType& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs);
+
+ /// @brief compares two JSON pointer for less-than
+ template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+ // NOLINTNEXTLINE(readability-redundant-declaration)
+ friend bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept;
+#endif
+
+ private:
+ /// the reference tokens
+ std::vector<string_t> reference_tokens;
+};
+
+#if !JSON_HAS_THREE_WAY_COMPARISON
+// functions cannot be defined inside class due to ODR violations
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+ return lhs.reference_tokens == rhs.reference_tokens;
+}
+
+template<typename RefStringTypeLhs,
+ typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const json_pointer<RefStringTypeLhs>& lhs,
+ const StringType& rhs)
+{
+ return lhs == json_pointer<RefStringTypeLhs>(rhs);
+}
+
+template<typename RefStringTypeRhs,
+ typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator==(json_pointer, json_pointer))
+inline bool operator==(const StringType& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs)
+{
+ return json_pointer<RefStringTypeRhs>(lhs) == rhs;
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+ return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs,
+ typename StringType = typename json_pointer<RefStringTypeLhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const json_pointer<RefStringTypeLhs>& lhs,
+ const StringType& rhs)
+{
+ return !(lhs == rhs);
+}
+
+template<typename RefStringTypeRhs,
+ typename StringType = typename json_pointer<RefStringTypeRhs>::string_t>
+JSON_HEDLEY_DEPRECATED_FOR(3.11.2, operator!=(json_pointer, json_pointer))
+inline bool operator!=(const StringType& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs)
+{
+ return !(lhs == rhs);
+}
+
+template<typename RefStringTypeLhs, typename RefStringTypeRhs>
+inline bool operator<(const json_pointer<RefStringTypeLhs>& lhs,
+ const json_pointer<RefStringTypeRhs>& rhs) noexcept
+{
+ return lhs.reference_tokens < rhs.reference_tokens;
+}
+#endif
+
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/json_ref.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <initializer_list>
+#include <utility>
+
+// #include <nlohmann/detail/abi_macros.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+template<typename BasicJsonType>
+class json_ref
+{
+ public:
+ using value_type = BasicJsonType;
+
+ json_ref(value_type&& value)
+ : owned_value(std::move(value))
+ {}
+
+ json_ref(const value_type& value)
+ : value_ref(&value)
+ {}
+
+ json_ref(std::initializer_list<json_ref> init)
+ : owned_value(init)
+ {}
+
+ template <
+ class... Args,
+ enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >
+ json_ref(Args && ... args)
+ : owned_value(std::forward<Args>(args)...)
+ {}
+
+ // class should be movable only
+ json_ref(json_ref&&) noexcept = default;
+ json_ref(const json_ref&) = delete;
+ json_ref& operator=(const json_ref&) = delete;
+ json_ref& operator=(json_ref&&) = delete;
+ ~json_ref() = default;
+
+ value_type moved_or_copied() const
+ {
+ if (value_ref == nullptr)
+ {
+ return std::move(owned_value);
+ }
+ return *value_ref;
+ }
+
+ value_type const& operator*() const
+ {
+ return value_ref ? *value_ref : owned_value;
+ }
+
+ value_type const* operator->() const
+ {
+ return &** this;
+ }
+
+ private:
+ mutable value_type owned_value = nullptr;
+ value_type const* value_ref = nullptr;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/string_escape.hpp>
+
+// #include <nlohmann/detail/string_utils.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+// #include <nlohmann/detail/output/binary_writer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // reverse
+#include <array> // array
+#include <map> // map
+#include <cmath> // isnan, isinf
+#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t
+#include <cstring> // memcpy
+#include <limits> // numeric_limits
+#include <string> // string
+#include <utility> // move
+#include <vector> // vector
+
+// #include <nlohmann/detail/input/binary_reader.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // copy
+#include <cstddef> // size_t
+#include <iterator> // back_inserter
+#include <memory> // shared_ptr, make_shared
+#include <string> // basic_string
+#include <vector> // vector
+
+#ifndef JSON_NO_IO
+ #include <ios> // streamsize
+ #include <ostream> // basic_ostream
+#endif // JSON_NO_IO
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// abstract output adapter interface
+template<typename CharType> struct output_adapter_protocol
+{
+ virtual void write_character(CharType c) = 0;
+ virtual void write_characters(const CharType* s, std::size_t length) = 0;
+ virtual ~output_adapter_protocol() = default;
+
+ output_adapter_protocol() = default;
+ output_adapter_protocol(const output_adapter_protocol&) = default;
+ output_adapter_protocol(output_adapter_protocol&&) noexcept = default;
+ output_adapter_protocol& operator=(const output_adapter_protocol&) = default;
+ output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default;
+};
+
+/// a type to simplify interfaces
+template<typename CharType>
+using output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;
+
+/// output adapter for byte vectors
+template<typename CharType, typename AllocatorType = std::allocator<CharType>>
+class output_vector_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_vector_adapter(std::vector<CharType, AllocatorType>& vec) noexcept
+ : v(vec)
+ {}
+
+ void write_character(CharType c) override
+ {
+ v.push_back(c);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ v.insert(v.end(), s, s + length);
+ }
+
+ private:
+ std::vector<CharType, AllocatorType>& v;
+};
+
+#ifndef JSON_NO_IO
+/// output adapter for output streams
+template<typename CharType>
+class output_stream_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept
+ : stream(s)
+ {}
+
+ void write_character(CharType c) override
+ {
+ stream.put(c);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ stream.write(s, static_cast<std::streamsize>(length));
+ }
+
+ private:
+ std::basic_ostream<CharType>& stream;
+};
+#endif // JSON_NO_IO
+
+/// output adapter for basic_string
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_string_adapter : public output_adapter_protocol<CharType>
+{
+ public:
+ explicit output_string_adapter(StringType& s) noexcept
+ : str(s)
+ {}
+
+ void write_character(CharType c) override
+ {
+ str.push_back(c);
+ }
+
+ JSON_HEDLEY_NON_NULL(2)
+ void write_characters(const CharType* s, std::size_t length) override
+ {
+ str.append(s, length);
+ }
+
+ private:
+ StringType& str;
+};
+
+template<typename CharType, typename StringType = std::basic_string<CharType>>
+class output_adapter
+{
+ public:
+ template<typename AllocatorType = std::allocator<CharType>>
+ output_adapter(std::vector<CharType, AllocatorType>& vec)
+ : oa(std::make_shared<output_vector_adapter<CharType, AllocatorType>>(vec)) {}
+
+#ifndef JSON_NO_IO
+ output_adapter(std::basic_ostream<CharType>& s)
+ : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}
+#endif // JSON_NO_IO
+
+ output_adapter(StringType& s)
+ : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}
+
+ operator output_adapter_t<CharType>()
+ {
+ return oa;
+ }
+
+ private:
+ output_adapter_t<CharType> oa = nullptr;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/// how to encode BJData
+enum class bjdata_version_t
+{
+ draft2,
+ draft3,
+};
+
+///////////////////
+// binary writer //
+///////////////////
+
+/*!
+@brief serialization to CBOR and MessagePack values
+*/
+template<typename BasicJsonType, typename CharType>
+class binary_writer
+{
+ using string_t = typename BasicJsonType::string_t;
+ using binary_t = typename BasicJsonType::binary_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+
+ public:
+ /*!
+ @brief create a binary writer
+
+ @param[in] adapter output adapter to write to
+ */
+ explicit binary_writer(output_adapter_t<CharType> adapter) : oa(std::move(adapter))
+ {
+ JSON_ASSERT(oa);
+ }
+
+ /*!
+ @param[in] j JSON value to serialize
+ @pre j.type() == value_t::object
+ */
+ void write_bson(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::object:
+ {
+ write_bson_object(*j.m_data.m_value.object);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::array:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ JSON_THROW(type_error::create(317, concat("to serialize to BSON, top-level type must be object, but is ", j.type_name()), &j));
+ }
+ }
+ }
+
+ /*!
+ @param[in] j JSON value to serialize
+ */
+ void write_cbor(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ oa->write_character(to_char_type(0xF6));
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ oa->write_character(j.m_data.m_value.boolean
+ ? to_char_type(0xF5)
+ : to_char_type(0xF4));
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_data.m_value.number_integer >= 0)
+ {
+ // CBOR does not differentiate between positive signed
+ // integers and unsigned integers. Therefore, we used the
+ // code from the value_t::number_unsigned case here.
+ if (j.m_data.m_value.number_integer <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x18));
+ write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x19));
+ write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x1A));
+ write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
+ }
+ else
+ {
+ oa->write_character(to_char_type(0x1B));
+ write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
+ }
+ }
+ else
+ {
+ // The conversions below encode the sign in the first
+ // byte, and the value is converted to a positive number.
+ const auto positive_number = -1 - j.m_data.m_value.number_integer;
+ if (j.m_data.m_value.number_integer >= -24)
+ {
+ write_number(static_cast<std::uint8_t>(0x20 + positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x38));
+ write_number(static_cast<std::uint8_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x39));
+ write_number(static_cast<std::uint16_t>(positive_number));
+ }
+ else if (positive_number <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x3A));
+ write_number(static_cast<std::uint32_t>(positive_number));
+ }
+ else
+ {
+ oa->write_character(to_char_type(0x3B));
+ write_number(static_cast<std::uint64_t>(positive_number));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_data.m_value.number_unsigned <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_unsigned));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x18));
+ write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_unsigned));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x19));
+ write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_unsigned));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x1A));
+ write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_unsigned));
+ }
+ else
+ {
+ oa->write_character(to_char_type(0x1B));
+ write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned));
+ }
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ if (std::isnan(j.m_data.m_value.number_float))
+ {
+ // NaN is 0xf97e00 in CBOR
+ oa->write_character(to_char_type(0xF9));
+ oa->write_character(to_char_type(0x7E));
+ oa->write_character(to_char_type(0x00));
+ }
+ else if (std::isinf(j.m_data.m_value.number_float))
+ {
+ // Infinity is 0xf97c00, -Infinity is 0xf9fc00
+ oa->write_character(to_char_type(0xf9));
+ oa->write_character(j.m_data.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC));
+ oa->write_character(to_char_type(0x00));
+ }
+ else
+ {
+ write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::cbor);
+ }
+ break;
+ }
+
+ case value_t::string:
+ {
+ // step 1: write control byte and the string length
+ const auto N = j.m_data.m_value.string->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(0x60 + N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x78));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x79));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x7A));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ oa->write_character(to_char_type(0x7B));
+ write_number(static_cast<std::uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write the string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
+ j.m_data.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_data.m_value.array->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(0x80 + N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x98));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x99));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x9A));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ oa->write_character(to_char_type(0x9B));
+ write_number(static_cast<std::uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_data.m_value.array)
+ {
+ write_cbor(el);
+ }
+ break;
+ }
+
+ case value_t::binary:
+ {
+ if (j.m_data.m_value.binary->has_subtype())
+ {
+ if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ write_number(static_cast<std::uint8_t>(0xd8));
+ write_number(static_cast<std::uint8_t>(j.m_data.m_value.binary->subtype()));
+ }
+ else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ write_number(static_cast<std::uint8_t>(0xd9));
+ write_number(static_cast<std::uint16_t>(j.m_data.m_value.binary->subtype()));
+ }
+ else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ write_number(static_cast<std::uint8_t>(0xda));
+ write_number(static_cast<std::uint32_t>(j.m_data.m_value.binary->subtype()));
+ }
+ else if (j.m_data.m_value.binary->subtype() <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ write_number(static_cast<std::uint8_t>(0xdb));
+ write_number(static_cast<std::uint64_t>(j.m_data.m_value.binary->subtype()));
+ }
+ }
+
+ // step 1: write control byte and the binary array size
+ const auto N = j.m_data.m_value.binary->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(0x40 + N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0x58));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0x59));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0x5A));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ oa->write_character(to_char_type(0x5B));
+ write_number(static_cast<std::uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
+ N);
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_data.m_value.object->size();
+ if (N <= 0x17)
+ {
+ write_number(static_cast<std::uint8_t>(0xA0 + N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ oa->write_character(to_char_type(0xB8));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ oa->write_character(to_char_type(0xB9));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ oa->write_character(to_char_type(0xBA));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+ // LCOV_EXCL_START
+ else if (N <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ oa->write_character(to_char_type(0xBB));
+ write_number(static_cast<std::uint64_t>(N));
+ }
+ // LCOV_EXCL_STOP
+
+ // step 2: write each element
+ for (const auto& el : *j.m_data.m_value.object)
+ {
+ write_cbor(el.first);
+ write_cbor(el.second);
+ }
+ break;
+ }
+
+ case value_t::discarded:
+ default:
+ break;
+ }
+ }
+
+ /*!
+ @param[in] j JSON value to serialize
+ */
+ void write_msgpack(const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::null: // nil
+ {
+ oa->write_character(to_char_type(0xC0));
+ break;
+ }
+
+ case value_t::boolean: // true and false
+ {
+ oa->write_character(j.m_data.m_value.boolean
+ ? to_char_type(0xC3)
+ : to_char_type(0xC2));
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ if (j.m_data.m_value.number_integer >= 0)
+ {
+ // MessagePack does not differentiate between positive
+ // signed integers and unsigned integers. Therefore, we used
+ // the code from the value_t::number_unsigned case here.
+ if (j.m_data.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ // uint 8
+ oa->write_character(to_char_type(0xCC));
+ write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // uint 16
+ oa->write_character(to_char_type(0xCD));
+ write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // uint 32
+ oa->write_character(to_char_type(0xCE));
+ write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ // uint 64
+ oa->write_character(to_char_type(0xCF));
+ write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
+ }
+ }
+ else
+ {
+ if (j.m_data.m_value.number_integer >= -32)
+ {
+ // negative fixnum
+ write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int8_t>::min)() &&
+ j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+ {
+ // int 8
+ oa->write_character(to_char_type(0xD0));
+ write_number(static_cast<std::int8_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int16_t>::min)() &&
+ j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+ {
+ // int 16
+ oa->write_character(to_char_type(0xD1));
+ write_number(static_cast<std::int16_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int32_t>::min)() &&
+ j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+ {
+ // int 32
+ oa->write_character(to_char_type(0xD2));
+ write_number(static_cast<std::int32_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_integer >= (std::numeric_limits<std::int64_t>::min)() &&
+ j.m_data.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+ {
+ // int 64
+ oa->write_character(to_char_type(0xD3));
+ write_number(static_cast<std::int64_t>(j.m_data.m_value.number_integer));
+ }
+ }
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_data.m_value.number_unsigned < 128)
+ {
+ // positive fixnum
+ write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ // uint 8
+ oa->write_character(to_char_type(0xCC));
+ write_number(static_cast<std::uint8_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // uint 16
+ oa->write_character(to_char_type(0xCD));
+ write_number(static_cast<std::uint16_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // uint 32
+ oa->write_character(to_char_type(0xCE));
+ write_number(static_cast<std::uint32_t>(j.m_data.m_value.number_integer));
+ }
+ else if (j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ // uint 64
+ oa->write_character(to_char_type(0xCF));
+ write_number(static_cast<std::uint64_t>(j.m_data.m_value.number_integer));
+ }
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ write_compact_float(j.m_data.m_value.number_float, detail::input_format_t::msgpack);
+ break;
+ }
+
+ case value_t::string:
+ {
+ // step 1: write control byte and the string length
+ const auto N = j.m_data.m_value.string->size();
+ if (N <= 31)
+ {
+ // fixstr
+ write_number(static_cast<std::uint8_t>(0xA0 | N));
+ }
+ else if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ // str 8
+ oa->write_character(to_char_type(0xD9));
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // str 16
+ oa->write_character(to_char_type(0xDA));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // str 32
+ oa->write_character(to_char_type(0xDB));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+
+ // step 2: write the string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
+ j.m_data.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ // step 1: write control byte and the array size
+ const auto N = j.m_data.m_value.array->size();
+ if (N <= 15)
+ {
+ // fixarray
+ write_number(static_cast<std::uint8_t>(0x90 | N));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // array 16
+ oa->write_character(to_char_type(0xDC));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // array 32
+ oa->write_character(to_char_type(0xDD));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_data.m_value.array)
+ {
+ write_msgpack(el);
+ }
+ break;
+ }
+
+ case value_t::binary:
+ {
+ // step 0: determine if the binary type has a set subtype to
+ // determine whether to use the ext or fixext types
+ const bool use_ext = j.m_data.m_value.binary->has_subtype();
+
+ // step 1: write control byte and the byte string length
+ const auto N = j.m_data.m_value.binary->size();
+ if (N <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ std::uint8_t output_type{};
+ bool fixed = true;
+ if (use_ext)
+ {
+ switch (N)
+ {
+ case 1:
+ output_type = 0xD4; // fixext 1
+ break;
+ case 2:
+ output_type = 0xD5; // fixext 2
+ break;
+ case 4:
+ output_type = 0xD6; // fixext 4
+ break;
+ case 8:
+ output_type = 0xD7; // fixext 8
+ break;
+ case 16:
+ output_type = 0xD8; // fixext 16
+ break;
+ default:
+ output_type = 0xC7; // ext 8
+ fixed = false;
+ break;
+ }
+
+ }
+ else
+ {
+ output_type = 0xC4; // bin 8
+ fixed = false;
+ }
+
+ oa->write_character(to_char_type(output_type));
+ if (!fixed)
+ {
+ write_number(static_cast<std::uint8_t>(N));
+ }
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ const std::uint8_t output_type = use_ext
+ ? 0xC8 // ext 16
+ : 0xC5; // bin 16
+
+ oa->write_character(to_char_type(output_type));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ const std::uint8_t output_type = use_ext
+ ? 0xC9 // ext 32
+ : 0xC6; // bin 32
+
+ oa->write_character(to_char_type(output_type));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+
+ // step 1.5: if this is an ext type, write the subtype
+ if (use_ext)
+ {
+ write_number(static_cast<std::int8_t>(j.m_data.m_value.binary->subtype()));
+ }
+
+ // step 2: write the byte string
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
+ N);
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ // step 1: write control byte and the object size
+ const auto N = j.m_data.m_value.object->size();
+ if (N <= 15)
+ {
+ // fixmap
+ write_number(static_cast<std::uint8_t>(0x80 | (N & 0xF)));
+ }
+ else if (N <= (std::numeric_limits<std::uint16_t>::max)())
+ {
+ // map 16
+ oa->write_character(to_char_type(0xDE));
+ write_number(static_cast<std::uint16_t>(N));
+ }
+ else if (N <= (std::numeric_limits<std::uint32_t>::max)())
+ {
+ // map 32
+ oa->write_character(to_char_type(0xDF));
+ write_number(static_cast<std::uint32_t>(N));
+ }
+
+ // step 2: write each element
+ for (const auto& el : *j.m_data.m_value.object)
+ {
+ write_msgpack(el.first);
+ write_msgpack(el.second);
+ }
+ break;
+ }
+
+ case value_t::discarded:
+ default:
+ break;
+ }
+ }
+
+ /*!
+ @param[in] j JSON value to serialize
+ @param[in] use_count whether to use '#' prefixes (optimized format)
+ @param[in] use_type whether to use '$' prefixes (optimized format)
+ @param[in] add_prefix whether prefixes need to be used for this value
+ @param[in] use_bjdata whether write in BJData format, default is false
+ @param[in] bjdata_version which BJData version to use, default is draft2
+ */
+ void write_ubjson(const BasicJsonType& j, const bool use_count,
+ const bool use_type, const bool add_prefix = true,
+ const bool use_bjdata = false, const bjdata_version_t bjdata_version = bjdata_version_t::draft2)
+ {
+ const bool bjdata_draft3 = use_bjdata && bjdata_version == bjdata_version_t::draft3;
+
+ switch (j.type())
+ {
+ case value_t::null:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('Z'));
+ }
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(j.m_data.m_value.boolean
+ ? to_char_type('T')
+ : to_char_type('F'));
+ }
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ write_number_with_ubjson_prefix(j.m_data.m_value.number_integer, add_prefix, use_bjdata);
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ write_number_with_ubjson_prefix(j.m_data.m_value.number_unsigned, add_prefix, use_bjdata);
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ write_number_with_ubjson_prefix(j.m_data.m_value.number_float, add_prefix, use_bjdata);
+ break;
+ }
+
+ case value_t::string:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('S'));
+ }
+ write_number_with_ubjson_prefix(j.m_data.m_value.string->size(), true, use_bjdata);
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_data.m_value.string->c_str()),
+ j.m_data.m_value.string->size());
+ break;
+ }
+
+ case value_t::array:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('['));
+ }
+
+ bool prefix_required = true;
+ if (use_type && !j.m_data.m_value.array->empty())
+ {
+ JSON_ASSERT(use_count);
+ const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+ const bool same_prefix = std::all_of(j.begin() + 1, j.end(),
+ [this, first_prefix, use_bjdata](const BasicJsonType & v)
+ {
+ return ubjson_prefix(v, use_bjdata) == first_prefix;
+ });
+
+ std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+ if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+ {
+ prefix_required = false;
+ oa->write_character(to_char_type('$'));
+ oa->write_character(first_prefix);
+ }
+ }
+
+ if (use_count)
+ {
+ oa->write_character(to_char_type('#'));
+ write_number_with_ubjson_prefix(j.m_data.m_value.array->size(), true, use_bjdata);
+ }
+
+ for (const auto& el : *j.m_data.m_value.array)
+ {
+ write_ubjson(el, use_count, use_type, prefix_required, use_bjdata, bjdata_version);
+ }
+
+ if (!use_count)
+ {
+ oa->write_character(to_char_type(']'));
+ }
+
+ break;
+ }
+
+ case value_t::binary:
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('['));
+ }
+
+ if (use_type && (bjdata_draft3 || !j.m_data.m_value.binary->empty()))
+ {
+ JSON_ASSERT(use_count);
+ oa->write_character(to_char_type('$'));
+ oa->write_character(bjdata_draft3 ? 'B' : 'U');
+ }
+
+ if (use_count)
+ {
+ oa->write_character(to_char_type('#'));
+ write_number_with_ubjson_prefix(j.m_data.m_value.binary->size(), true, use_bjdata);
+ }
+
+ if (use_type)
+ {
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(j.m_data.m_value.binary->data()),
+ j.m_data.m_value.binary->size());
+ }
+ else
+ {
+ for (size_t i = 0; i < j.m_data.m_value.binary->size(); ++i)
+ {
+ oa->write_character(to_char_type(bjdata_draft3 ? 'B' : 'U'));
+ oa->write_character(j.m_data.m_value.binary->data()[i]);
+ }
+ }
+
+ if (!use_count)
+ {
+ oa->write_character(to_char_type(']'));
+ }
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ if (use_bjdata && j.m_data.m_value.object->size() == 3 && j.m_data.m_value.object->find("_ArrayType_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArraySize_") != j.m_data.m_value.object->end() && j.m_data.m_value.object->find("_ArrayData_") != j.m_data.m_value.object->end())
+ {
+ if (!write_bjdata_ndarray(*j.m_data.m_value.object, use_count, use_type, bjdata_version)) // decode bjdata ndarray in the JData format (https://github.com/NeuroJSON/jdata)
+ {
+ break;
+ }
+ }
+
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('{'));
+ }
+
+ bool prefix_required = true;
+ if (use_type && !j.m_data.m_value.object->empty())
+ {
+ JSON_ASSERT(use_count);
+ const CharType first_prefix = ubjson_prefix(j.front(), use_bjdata);
+ const bool same_prefix = std::all_of(j.begin(), j.end(),
+ [this, first_prefix, use_bjdata](const BasicJsonType & v)
+ {
+ return ubjson_prefix(v, use_bjdata) == first_prefix;
+ });
+
+ std::vector<CharType> bjdx = {'[', '{', 'S', 'H', 'T', 'F', 'N', 'Z'}; // excluded markers in bjdata optimized type
+
+ if (same_prefix && !(use_bjdata && std::find(bjdx.begin(), bjdx.end(), first_prefix) != bjdx.end()))
+ {
+ prefix_required = false;
+ oa->write_character(to_char_type('$'));
+ oa->write_character(first_prefix);
+ }
+ }
+
+ if (use_count)
+ {
+ oa->write_character(to_char_type('#'));
+ write_number_with_ubjson_prefix(j.m_data.m_value.object->size(), true, use_bjdata);
+ }
+
+ for (const auto& el : *j.m_data.m_value.object)
+ {
+ write_number_with_ubjson_prefix(el.first.size(), true, use_bjdata);
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(el.first.c_str()),
+ el.first.size());
+ write_ubjson(el.second, use_count, use_type, prefix_required, use_bjdata, bjdata_version);
+ }
+
+ if (!use_count)
+ {
+ oa->write_character(to_char_type('}'));
+ }
+
+ break;
+ }
+
+ case value_t::discarded:
+ default:
+ break;
+ }
+ }
+
+ private:
+ //////////
+ // BSON //
+ //////////
+
+ /*!
+ @return The size of a BSON document entry header, including the id marker
+ and the entry name size (and its null-terminator).
+ */
+ static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
+ {
+ const auto it = name.find(static_cast<typename string_t::value_type>(0));
+ if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
+ {
+ JSON_THROW(out_of_range::create(409, concat("BSON key cannot contain code point U+0000 (at byte ", std::to_string(it), ")"), &j));
+ static_cast<void>(j);
+ }
+
+ return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
+ }
+
+ /*!
+ @brief Writes the given @a element_type and @a name to the output adapter
+ */
+ void write_bson_entry_header(const string_t& name,
+ const std::uint8_t element_type)
+ {
+ oa->write_character(to_char_type(element_type)); // boolean
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(name.c_str()),
+ name.size() + 1u);
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and boolean value @a value
+ */
+ void write_bson_boolean(const string_t& name,
+ const bool value)
+ {
+ write_bson_entry_header(name, 0x08);
+ oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and double value @a value
+ */
+ void write_bson_double(const string_t& name,
+ const double value)
+ {
+ write_bson_entry_header(name, 0x01);
+ write_number<double>(value, true);
+ }
+
+ /*!
+ @return The size of the BSON-encoded string in @a value
+ */
+ static std::size_t calc_bson_string_size(const string_t& value)
+ {
+ return sizeof(std::int32_t) + value.size() + 1ul;
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and string value @a value
+ */
+ void write_bson_string(const string_t& name,
+ const string_t& value)
+ {
+ write_bson_entry_header(name, 0x02);
+
+ write_number<std::int32_t>(static_cast<std::int32_t>(value.size() + 1ul), true);
+ oa->write_characters(
+ reinterpret_cast<const CharType*>(value.c_str()),
+ value.size() + 1);
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and null value
+ */
+ void write_bson_null(const string_t& name)
+ {
+ write_bson_entry_header(name, 0x0A);
+ }
+
+ /*!
+ @return The size of the BSON-encoded integer @a value
+ */
+ static std::size_t calc_bson_integer_size(const std::int64_t value)
+ {
+ return (std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)()
+ ? sizeof(std::int32_t)
+ : sizeof(std::int64_t);
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and integer @a value
+ */
+ void write_bson_integer(const string_t& name,
+ const std::int64_t value)
+ {
+ if ((std::numeric_limits<std::int32_t>::min)() <= value && value <= (std::numeric_limits<std::int32_t>::max)())
+ {
+ write_bson_entry_header(name, 0x10); // int32
+ write_number<std::int32_t>(static_cast<std::int32_t>(value), true);
+ }
+ else
+ {
+ write_bson_entry_header(name, 0x12); // int64
+ write_number<std::int64_t>(static_cast<std::int64_t>(value), true);
+ }
+ }
+
+ /*!
+ @return The size of the BSON-encoded unsigned integer in @a j
+ */
+ static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept
+ {
+ return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+ ? sizeof(std::int32_t)
+ : sizeof(std::int64_t);
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and unsigned @a value
+ */
+ void write_bson_unsigned(const string_t& name,
+ const BasicJsonType& j)
+ {
+ if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+ {
+ write_bson_entry_header(name, 0x10 /* int32 */);
+ write_number<std::int32_t>(static_cast<std::int32_t>(j.m_data.m_value.number_unsigned), true);
+ }
+ else if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+ {
+ write_bson_entry_header(name, 0x12 /* int64 */);
+ write_number<std::int64_t>(static_cast<std::int64_t>(j.m_data.m_value.number_unsigned), true);
+ }
+ else
+ {
+ write_bson_entry_header(name, 0x11 /* uint64 */);
+ write_number<std::uint64_t>(static_cast<std::uint64_t>(j.m_data.m_value.number_unsigned), true);
+ }
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and object @a value
+ */
+ void write_bson_object_entry(const string_t& name,
+ const typename BasicJsonType::object_t& value)
+ {
+ write_bson_entry_header(name, 0x03); // object
+ write_bson_object(value);
+ }
+
+ /*!
+ @return The size of the BSON-encoded array @a value
+ */
+ static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)
+ {
+ std::size_t array_index = 0ul;
+
+ const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast<std::size_t>(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el)
+ {
+ return result + calc_bson_element_size(std::to_string(array_index++), el);
+ });
+
+ return sizeof(std::int32_t) + embedded_document_size + 1ul;
+ }
+
+ /*!
+ @return The size of the BSON-encoded binary array @a value
+ */
+ static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value)
+ {
+ return sizeof(std::int32_t) + value.size() + 1ul;
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and array @a value
+ */
+ void write_bson_array(const string_t& name,
+ const typename BasicJsonType::array_t& value)
+ {
+ write_bson_entry_header(name, 0x04); // array
+ write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_array_size(value)), true);
+
+ std::size_t array_index = 0ul;
+
+ for (const auto& el : value)
+ {
+ write_bson_element(std::to_string(array_index++), el);
+ }
+
+ oa->write_character(to_char_type(0x00));
+ }
+
+ /*!
+ @brief Writes a BSON element with key @a name and binary value @a value
+ */
+ void write_bson_binary(const string_t& name,
+ const binary_t& value)
+ {
+ write_bson_entry_header(name, 0x05);
+
+ write_number<std::int32_t>(static_cast<std::int32_t>(value.size()), true);
+ write_number(value.has_subtype() ? static_cast<std::uint8_t>(value.subtype()) : static_cast<std::uint8_t>(0x00));
+
+ oa->write_characters(reinterpret_cast<const CharType*>(value.data()), value.size());
+ }
+
+ /*!
+ @brief Calculates the size necessary to serialize the JSON value @a j with its @a name
+ @return The calculated size for the BSON document entry for @a j with the given @a name.
+ */
+ static std::size_t calc_bson_element_size(const string_t& name,
+ const BasicJsonType& j)
+ {
+ const auto header_size = calc_bson_entry_header_size(name, j);
+ switch (j.type())
+ {
+ case value_t::object:
+ return header_size + calc_bson_object_size(*j.m_data.m_value.object);
+
+ case value_t::array:
+ return header_size + calc_bson_array_size(*j.m_data.m_value.array);
+
+ case value_t::binary:
+ return header_size + calc_bson_binary_size(*j.m_data.m_value.binary);
+
+ case value_t::boolean:
+ return header_size + 1ul;
+
+ case value_t::number_float:
+ return header_size + 8ul;
+
+ case value_t::number_integer:
+ return header_size + calc_bson_integer_size(j.m_data.m_value.number_integer);
+
+ case value_t::number_unsigned:
+ return header_size + calc_bson_unsigned_size(j.m_data.m_value.number_unsigned);
+
+ case value_t::string:
+ return header_size + calc_bson_string_size(*j.m_data.m_value.string);
+
+ case value_t::null:
+ return header_size + 0ul;
+
+ // LCOV_EXCL_START
+ case value_t::discarded:
+ default:
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+ return 0ul;
+ // LCOV_EXCL_STOP
+ }
+ }
+
+ /*!
+ @brief Serializes the JSON value @a j to BSON and associates it with the
+ key @a name.
+ @param name The name to associate with the JSON entity @a j within the
+ current BSON document
+ */
+ void write_bson_element(const string_t& name,
+ const BasicJsonType& j)
+ {
+ switch (j.type())
+ {
+ case value_t::object:
+ return write_bson_object_entry(name, *j.m_data.m_value.object);
+
+ case value_t::array:
+ return write_bson_array(name, *j.m_data.m_value.array);
+
+ case value_t::binary:
+ return write_bson_binary(name, *j.m_data.m_value.binary);
+
+ case value_t::boolean:
+ return write_bson_boolean(name, j.m_data.m_value.boolean);
+
+ case value_t::number_float:
+ return write_bson_double(name, j.m_data.m_value.number_float);
+
+ case value_t::number_integer:
+ return write_bson_integer(name, j.m_data.m_value.number_integer);
+
+ case value_t::number_unsigned:
+ return write_bson_unsigned(name, j);
+
+ case value_t::string:
+ return write_bson_string(name, *j.m_data.m_value.string);
+
+ case value_t::null:
+ return write_bson_null(name);
+
+ // LCOV_EXCL_START
+ case value_t::discarded:
+ default:
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert)
+ return;
+ // LCOV_EXCL_STOP
+ }
+ }
+
+ /*!
+ @brief Calculates the size of the BSON serialization of the given
+ JSON-object @a j.
+ @param[in] value JSON value to serialize
+ @pre value.type() == value_t::object
+ */
+ static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)
+ {
+ const std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast<std::size_t>(0),
+ [](size_t result, const typename BasicJsonType::object_t::value_type & el)
+ {
+ return result += calc_bson_element_size(el.first, el.second);
+ });
+
+ return sizeof(std::int32_t) + document_size + 1ul;
+ }
+
+ /*!
+ @param[in] value JSON value to serialize
+ @pre value.type() == value_t::object
+ */
+ void write_bson_object(const typename BasicJsonType::object_t& value)
+ {
+ write_number<std::int32_t>(static_cast<std::int32_t>(calc_bson_object_size(value)), true);
+
+ for (const auto& el : value)
+ {
+ write_bson_element(el.first, el.second);
+ }
+
+ oa->write_character(to_char_type(0x00));
+ }
+
+ //////////
+ // CBOR //
+ //////////
+
+ static constexpr CharType get_cbor_float_prefix(float /*unused*/)
+ {
+ return to_char_type(0xFA); // Single-Precision Float
+ }
+
+ static constexpr CharType get_cbor_float_prefix(double /*unused*/)
+ {
+ return to_char_type(0xFB); // Double-Precision Float
+ }
+
+ /////////////
+ // MsgPack //
+ /////////////
+
+ static constexpr CharType get_msgpack_float_prefix(float /*unused*/)
+ {
+ return to_char_type(0xCA); // float 32
+ }
+
+ static constexpr CharType get_msgpack_float_prefix(double /*unused*/)
+ {
+ return to_char_type(0xCB); // float 64
+ }
+
+ ////////////
+ // UBJSON //
+ ////////////
+
+ // UBJSON: write number (floating point)
+ template<typename NumberType, typename std::enable_if<
+ std::is_floating_point<NumberType>::value, int>::type = 0>
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix,
+ const bool use_bjdata)
+ {
+ if (add_prefix)
+ {
+ oa->write_character(get_ubjson_float_prefix(n));
+ }
+ write_number(n, use_bjdata);
+ }
+
+ // UBJSON: write number (unsigned integer)
+ template<typename NumberType, typename std::enable_if<
+ std::is_unsigned<NumberType>::value, int>::type = 0>
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix,
+ const bool use_bjdata)
+ {
+ if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('i')); // int8
+ }
+ write_number(static_cast<std::uint8_t>(n), use_bjdata);
+ }
+ else if (n <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('U')); // uint8
+ }
+ write_number(static_cast<std::uint8_t>(n), use_bjdata);
+ }
+ else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('I')); // int16
+ }
+ write_number(static_cast<std::int16_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint16_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('u')); // uint16 - bjdata only
+ }
+ write_number(static_cast<std::uint16_t>(n), use_bjdata);
+ }
+ else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('l')); // int32
+ }
+ write_number(static_cast<std::int32_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && n <= static_cast<uint64_t>((std::numeric_limits<uint32_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('m')); // uint32 - bjdata only
+ }
+ write_number(static_cast<std::uint32_t>(n), use_bjdata);
+ }
+ else if (n <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('L')); // int64
+ }
+ write_number(static_cast<std::int64_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && n <= (std::numeric_limits<uint64_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('M')); // uint64 - bjdata only
+ }
+ write_number(static_cast<std::uint64_t>(n), use_bjdata);
+ }
+ else
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('H')); // high-precision number
+ }
+
+ const auto number = BasicJsonType(n).dump();
+ write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+ for (std::size_t i = 0; i < number.size(); ++i)
+ {
+ oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+ }
+ }
+ }
+
+ // UBJSON: write number (signed integer)
+ template < typename NumberType, typename std::enable_if <
+ std::is_signed<NumberType>::value&&
+ !std::is_floating_point<NumberType>::value, int >::type = 0 >
+ void write_number_with_ubjson_prefix(const NumberType n,
+ const bool add_prefix,
+ const bool use_bjdata)
+ {
+ if ((std::numeric_limits<std::int8_t>::min)() <= n && n <= (std::numeric_limits<std::int8_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('i')); // int8
+ }
+ write_number(static_cast<std::int8_t>(n), use_bjdata);
+ }
+ else if (static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint8_t>::max)()))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('U')); // uint8
+ }
+ write_number(static_cast<std::uint8_t>(n), use_bjdata);
+ }
+ else if ((std::numeric_limits<std::int16_t>::min)() <= n && n <= (std::numeric_limits<std::int16_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('I')); // int16
+ }
+ write_number(static_cast<std::int16_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint16_t>::max)())))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('u')); // uint16 - bjdata only
+ }
+ write_number(static_cast<uint16_t>(n), use_bjdata);
+ }
+ else if ((std::numeric_limits<std::int32_t>::min)() <= n && n <= (std::numeric_limits<std::int32_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('l')); // int32
+ }
+ write_number(static_cast<std::int32_t>(n), use_bjdata);
+ }
+ else if (use_bjdata && (static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::min)()) <= n && n <= static_cast<std::int64_t>((std::numeric_limits<std::uint32_t>::max)())))
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('m')); // uint32 - bjdata only
+ }
+ write_number(static_cast<uint32_t>(n), use_bjdata);
+ }
+ else if ((std::numeric_limits<std::int64_t>::min)() <= n && n <= (std::numeric_limits<std::int64_t>::max)())
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('L')); // int64
+ }
+ write_number(static_cast<std::int64_t>(n), use_bjdata);
+ }
+ // LCOV_EXCL_START
+ else
+ {
+ if (add_prefix)
+ {
+ oa->write_character(to_char_type('H')); // high-precision number
+ }
+
+ const auto number = BasicJsonType(n).dump();
+ write_number_with_ubjson_prefix(number.size(), true, use_bjdata);
+ for (std::size_t i = 0; i < number.size(); ++i)
+ {
+ oa->write_character(to_char_type(static_cast<std::uint8_t>(number[i])));
+ }
+ }
+ // LCOV_EXCL_STOP
+ }
+
+ /*!
+ @brief determine the type prefix of container values
+ */
+ CharType ubjson_prefix(const BasicJsonType& j, const bool use_bjdata) const noexcept
+ {
+ switch (j.type())
+ {
+ case value_t::null:
+ return 'Z';
+
+ case value_t::boolean:
+ return j.m_data.m_value.boolean ? 'T' : 'F';
+
+ case value_t::number_integer:
+ {
+ if ((std::numeric_limits<std::int8_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int8_t>::max)())
+ {
+ return 'i';
+ }
+ if ((std::numeric_limits<std::uint8_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint8_t>::max)())
+ {
+ return 'U';
+ }
+ if ((std::numeric_limits<std::int16_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int16_t>::max)())
+ {
+ return 'I';
+ }
+ if (use_bjdata && ((std::numeric_limits<std::uint16_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint16_t>::max)()))
+ {
+ return 'u';
+ }
+ if ((std::numeric_limits<std::int32_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int32_t>::max)())
+ {
+ return 'l';
+ }
+ if (use_bjdata && ((std::numeric_limits<std::uint32_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::uint32_t>::max)()))
+ {
+ return 'm';
+ }
+ if ((std::numeric_limits<std::int64_t>::min)() <= j.m_data.m_value.number_integer && j.m_data.m_value.number_integer <= (std::numeric_limits<std::int64_t>::max)())
+ {
+ return 'L';
+ }
+ // anything else is treated as high-precision number
+ return 'H'; // LCOV_EXCL_LINE
+ }
+
+ case value_t::number_unsigned:
+ {
+ if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int8_t>::max)()))
+ {
+ return 'i';
+ }
+ if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint8_t>::max)()))
+ {
+ return 'U';
+ }
+ if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int16_t>::max)()))
+ {
+ return 'I';
+ }
+ if (use_bjdata && j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint16_t>::max)()))
+ {
+ return 'u';
+ }
+ if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
+ {
+ return 'l';
+ }
+ if (use_bjdata && j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::uint32_t>::max)()))
+ {
+ return 'm';
+ }
+ if (j.m_data.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
+ {
+ return 'L';
+ }
+ if (use_bjdata && j.m_data.m_value.number_unsigned <= (std::numeric_limits<std::uint64_t>::max)())
+ {
+ return 'M';
+ }
+ // anything else is treated as high-precision number
+ return 'H'; // LCOV_EXCL_LINE
+ }
+
+ case value_t::number_float:
+ return get_ubjson_float_prefix(j.m_data.m_value.number_float);
+
+ case value_t::string:
+ return 'S';
+
+ case value_t::array: // fallthrough
+ case value_t::binary:
+ return '[';
+
+ case value_t::object:
+ return '{';
+
+ case value_t::discarded:
+ default: // discarded values
+ return 'N';
+ }
+ }
+
+ static constexpr CharType get_ubjson_float_prefix(float /*unused*/)
+ {
+ return 'd'; // float 32
+ }
+
+ static constexpr CharType get_ubjson_float_prefix(double /*unused*/)
+ {
+ return 'D'; // float 64
+ }
+
+ /*!
+ @return false if the object is successfully converted to a bjdata ndarray, true if the type or size is invalid
+ */
+ bool write_bjdata_ndarray(const typename BasicJsonType::object_t& value, const bool use_count, const bool use_type, const bjdata_version_t bjdata_version)
+ {
+ std::map<string_t, CharType> bjdtype = {{"uint8", 'U'}, {"int8", 'i'}, {"uint16", 'u'}, {"int16", 'I'},
+ {"uint32", 'm'}, {"int32", 'l'}, {"uint64", 'M'}, {"int64", 'L'}, {"single", 'd'}, {"double", 'D'},
+ {"char", 'C'}, {"byte", 'B'}
+ };
+
+ string_t key = "_ArrayType_";
+ auto it = bjdtype.find(static_cast<string_t>(value.at(key)));
+ if (it == bjdtype.end())
+ {
+ return true;
+ }
+ CharType dtype = it->second;
+
+ key = "_ArraySize_";
+ std::size_t len = (value.at(key).empty() ? 0 : 1);
+ for (const auto& el : value.at(key))
+ {
+ len *= static_cast<std::size_t>(el.m_data.m_value.number_unsigned);
+ }
+
+ key = "_ArrayData_";
+ if (value.at(key).size() != len)
+ {
+ return true;
+ }
+
+ oa->write_character('[');
+ oa->write_character('$');
+ oa->write_character(dtype);
+ oa->write_character('#');
+
+ key = "_ArraySize_";
+ write_ubjson(value.at(key), use_count, use_type, true, true, bjdata_version);
+
+ key = "_ArrayData_";
+ if (dtype == 'U' || dtype == 'C' || dtype == 'B')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::uint8_t>(el.m_data.m_value.number_unsigned), true);
+ }
+ }
+ else if (dtype == 'i')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::int8_t>(el.m_data.m_value.number_integer), true);
+ }
+ }
+ else if (dtype == 'u')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::uint16_t>(el.m_data.m_value.number_unsigned), true);
+ }
+ }
+ else if (dtype == 'I')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::int16_t>(el.m_data.m_value.number_integer), true);
+ }
+ }
+ else if (dtype == 'm')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::uint32_t>(el.m_data.m_value.number_unsigned), true);
+ }
+ }
+ else if (dtype == 'l')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::int32_t>(el.m_data.m_value.number_integer), true);
+ }
+ }
+ else if (dtype == 'M')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::uint64_t>(el.m_data.m_value.number_unsigned), true);
+ }
+ }
+ else if (dtype == 'L')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<std::int64_t>(el.m_data.m_value.number_integer), true);
+ }
+ }
+ else if (dtype == 'd')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<float>(el.m_data.m_value.number_float), true);
+ }
+ }
+ else if (dtype == 'D')
+ {
+ for (const auto& el : value.at(key))
+ {
+ write_number(static_cast<double>(el.m_data.m_value.number_float), true);
+ }
+ }
+ return false;
+ }
+
+ ///////////////////////
+ // Utility functions //
+ ///////////////////////
+
+ /*
+ @brief write a number to output input
+ @param[in] n number of type @a NumberType
+ @param[in] OutputIsLittleEndian Set to true if output data is
+ required to be little endian
+ @tparam NumberType the type of the number
+
+ @note This function needs to respect the system's endianness, because bytes
+ in CBOR, MessagePack, and UBJSON are stored in network order (big
+ endian) and therefore need reordering on little endian systems.
+ On the other hand, BSON and BJData use little endian and should reorder
+ on big endian systems.
+ */
+ template<typename NumberType>
+ void write_number(const NumberType n, const bool OutputIsLittleEndian = false)
+ {
+ // step 1: write number to array of length NumberType
+ std::array<CharType, sizeof(NumberType)> vec{};
+ std::memcpy(vec.data(), &n, sizeof(NumberType));
+
+ // step 2: write array to output (with possible reordering)
+ if (is_little_endian != OutputIsLittleEndian)
+ {
+ // reverse byte order prior to conversion if necessary
+ std::reverse(vec.begin(), vec.end());
+ }
+
+ oa->write_characters(vec.data(), sizeof(NumberType));
+ }
+
+ void write_compact_float(const number_float_t n, detail::input_format_t format)
+ {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ if (static_cast<double>(n) >= static_cast<double>(std::numeric_limits<float>::lowest()) &&
+ static_cast<double>(n) <= static_cast<double>((std::numeric_limits<float>::max)()) &&
+ static_cast<double>(static_cast<float>(n)) == static_cast<double>(n))
+ {
+ oa->write_character(format == detail::input_format_t::cbor
+ ? get_cbor_float_prefix(static_cast<float>(n))
+ : get_msgpack_float_prefix(static_cast<float>(n)));
+ write_number(static_cast<float>(n));
+ }
+ else
+ {
+ oa->write_character(format == detail::input_format_t::cbor
+ ? get_cbor_float_prefix(n)
+ : get_msgpack_float_prefix(n));
+ write_number(n);
+ }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ }
+
+ public:
+ // The following to_char_type functions are implement the conversion
+ // between uint8_t and CharType. In case CharType is not unsigned,
+ // such a conversion is required to allow values greater than 128.
+ // See <https://github.com/nlohmann/json/issues/1286> for a discussion.
+ template < typename C = CharType,
+ enable_if_t < std::is_signed<C>::value && std::is_signed<char>::value > * = nullptr >
+ static constexpr CharType to_char_type(std::uint8_t x) noexcept
+ {
+ return *reinterpret_cast<char*>(&x);
+ }
+
+ template < typename C = CharType,
+ enable_if_t < std::is_signed<C>::value && std::is_unsigned<char>::value > * = nullptr >
+ static CharType to_char_type(std::uint8_t x) noexcept
+ {
+ static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t");
+ static_assert(std::is_trivial<CharType>::value, "CharType must be trivial");
+ CharType result;
+ std::memcpy(&result, &x, sizeof(x));
+ return result;
+ }
+
+ template<typename C = CharType,
+ enable_if_t<std::is_unsigned<C>::value>* = nullptr>
+ static constexpr CharType to_char_type(std::uint8_t x) noexcept
+ {
+ return x;
+ }
+
+ template < typename InputCharType, typename C = CharType,
+ enable_if_t <
+ std::is_signed<C>::value &&
+ std::is_signed<char>::value &&
+ std::is_same<char, typename std::remove_cv<InputCharType>::type>::value
+ > * = nullptr >
+ static constexpr CharType to_char_type(InputCharType x) noexcept
+ {
+ return x;
+ }
+
+ private:
+ /// whether we can assume little endianness
+ const bool is_little_endian = little_endianness();
+
+ /// the output
+ output_adapter_t<CharType> oa = nullptr;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+// #include <nlohmann/detail/output/serializer.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2008 - 2009 Björn Hoehrmann <bjoern@hoehrmann.de>
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <algorithm> // reverse, remove, fill, find, none_of
+#include <array> // array
+#include <clocale> // localeconv, lconv
+#include <cmath> // labs, isfinite, isnan, signbit
+#include <cstddef> // size_t, ptrdiff_t
+#include <cstdint> // uint8_t
+#include <cstdio> // snprintf
+#include <limits> // numeric_limits
+#include <string> // string, char_traits
+#include <iomanip> // setfill, setw
+#include <type_traits> // is_same
+#include <utility> // move
+
+// #include <nlohmann/detail/conversions/to_chars.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2009 Florian Loitsch <https://florian.loitsch.com/>
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <array> // array
+#include <cmath> // signbit, isfinite
+#include <cstdint> // intN_t, uintN_t
+#include <cstring> // memcpy, memmove
+#include <limits> // numeric_limits
+#include <type_traits> // conditional
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+/*!
+@brief implements the Grisu2 algorithm for binary to decimal floating-point
+conversion.
+
+This implementation is a slightly modified version of the reference
+implementation which may be obtained from
+http://florian.loitsch.com/publications (bench.tar.gz).
+
+The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.
+
+For a detailed description of the algorithm see:
+
+[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with
+ Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming
+ Language Design and Implementation, PLDI 2010
+[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately",
+ Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language
+ Design and Implementation, PLDI 1996
+*/
+namespace dtoa_impl
+{
+
+template<typename Target, typename Source>
+Target reinterpret_bits(const Source source)
+{
+ static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
+
+ Target target;
+ std::memcpy(&target, &source, sizeof(Source));
+ return target;
+}
+
+struct diyfp // f * 2^e
+{
+ static constexpr int kPrecision = 64; // = q
+
+ std::uint64_t f = 0;
+ int e = 0;
+
+ constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
+
+ /*!
+ @brief returns x - y
+ @pre x.e == y.e and x.f >= y.f
+ */
+ static diyfp sub(const diyfp& x, const diyfp& y) noexcept
+ {
+ JSON_ASSERT(x.e == y.e);
+ JSON_ASSERT(x.f >= y.f);
+
+ return {x.f - y.f, x.e};
+ }
+
+ /*!
+ @brief returns x * y
+ @note The result is rounded. (Only the upper q bits are returned.)
+ */
+ static diyfp mul(const diyfp& x, const diyfp& y) noexcept
+ {
+ static_assert(kPrecision == 64, "internal error");
+
+ // Computes:
+ // f = round((x.f * y.f) / 2^q)
+ // e = x.e + y.e + q
+
+ // Emulate the 64-bit * 64-bit multiplication:
+ //
+ // p = u * v
+ // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)
+ // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi )
+ // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 )
+ // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 )
+ // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3)
+ // = (p0_lo ) + 2^32 (Q ) + 2^64 (H )
+ // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H )
+ //
+ // (Since Q might be larger than 2^32 - 1)
+ //
+ // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)
+ //
+ // (Q_hi + H does not overflow a 64-bit int)
+ //
+ // = p_lo + 2^64 p_hi
+
+ const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;
+ const std::uint64_t u_hi = x.f >> 32u;
+ const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;
+ const std::uint64_t v_hi = y.f >> 32u;
+
+ const std::uint64_t p0 = u_lo * v_lo;
+ const std::uint64_t p1 = u_lo * v_hi;
+ const std::uint64_t p2 = u_hi * v_lo;
+ const std::uint64_t p3 = u_hi * v_hi;
+
+ const std::uint64_t p0_hi = p0 >> 32u;
+ const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;
+ const std::uint64_t p1_hi = p1 >> 32u;
+ const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;
+ const std::uint64_t p2_hi = p2 >> 32u;
+
+ std::uint64_t Q = p0_hi + p1_lo + p2_lo;
+
+ // The full product might now be computed as
+ //
+ // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)
+ // p_lo = p0_lo + (Q << 32)
+ //
+ // But in this particular case here, the full p_lo is not required.
+ // Effectively we only need to add the highest bit in p_lo to p_hi (and
+ // Q_hi + 1 does not overflow).
+
+ Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up
+
+ const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);
+
+ return {h, x.e + y.e + 64};
+ }
+
+ /*!
+ @brief normalize x such that the significand is >= 2^(q-1)
+ @pre x.f != 0
+ */
+ static diyfp normalize(diyfp x) noexcept
+ {
+ JSON_ASSERT(x.f != 0);
+
+ while ((x.f >> 63u) == 0)
+ {
+ x.f <<= 1u;
+ x.e--;
+ }
+
+ return x;
+ }
+
+ /*!
+ @brief normalize x such that the result has the exponent E
+ @pre e >= x.e and the upper e - x.e bits of x.f must be zero.
+ */
+ static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept
+ {
+ const int delta = x.e - target_exponent;
+
+ JSON_ASSERT(delta >= 0);
+ JSON_ASSERT(((x.f << delta) >> delta) == x.f);
+
+ return {x.f << delta, target_exponent};
+ }
+};
+
+struct boundaries
+{
+ diyfp w;
+ diyfp minus;
+ diyfp plus;
+};
+
+/*!
+Compute the (normalized) diyfp representing the input number 'value' and its
+boundaries.
+
+@pre value must be finite and positive
+*/
+template<typename FloatType>
+boundaries compute_boundaries(FloatType value)
+{
+ JSON_ASSERT(std::isfinite(value));
+ JSON_ASSERT(value > 0);
+
+ // Convert the IEEE representation into a diyfp.
+ //
+ // If v is denormal:
+ // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1))
+ // If v is normalized:
+ // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))
+
+ static_assert(std::numeric_limits<FloatType>::is_iec559,
+ "internal error: dtoa_short requires an IEEE-754 floating-point implementation");
+
+ constexpr int kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)
+ constexpr int kBias = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);
+ constexpr int kMinExp = 1 - kBias;
+ constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1)
+
+ using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t, std::uint64_t >::type;
+
+ const auto bits = static_cast<std::uint64_t>(reinterpret_bits<bits_type>(value));
+ const std::uint64_t E = bits >> (kPrecision - 1);
+ const std::uint64_t F = bits & (kHiddenBit - 1);
+
+ const bool is_denormal = E == 0;
+ const diyfp v = is_denormal
+ ? diyfp(F, kMinExp)
+ : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
+
+ // Compute the boundaries m- and m+ of the floating-point value
+ // v = f * 2^e.
+ //
+ // Determine v- and v+, the floating-point predecessor and successor if v,
+ // respectively.
+ //
+ // v- = v - 2^e if f != 2^(p-1) or e == e_min (A)
+ // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B)
+ //
+ // v+ = v + 2^e
+ //
+ // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_
+ // between m- and m+ round to v, regardless of how the input rounding
+ // algorithm breaks ties.
+ //
+ // ---+-------------+-------------+-------------+-------------+--- (A)
+ // v- m- v m+ v+
+ //
+ // -----------------+------+------+-------------+-------------+--- (B)
+ // v- m- v m+ v+
+
+ const bool lower_boundary_is_closer = F == 0 && E > 1;
+ const diyfp m_plus = diyfp((2 * v.f) + 1, v.e - 1);
+ const diyfp m_minus = lower_boundary_is_closer
+ ? diyfp((4 * v.f) - 1, v.e - 2) // (B)
+ : diyfp((2 * v.f) - 1, v.e - 1); // (A)
+
+ // Determine the normalized w+ = m+.
+ const diyfp w_plus = diyfp::normalize(m_plus);
+
+ // Determine w- = m- such that e_(w-) = e_(w+).
+ const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);
+
+ return {diyfp::normalize(v), w_minus, w_plus};
+}
+
+// Given normalized diyfp w, Grisu needs to find a (normalized) cached
+// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies
+// within a certain range [alpha, gamma] (Definition 3.2 from [1])
+//
+// alpha <= e = e_c + e_w + q <= gamma
+//
+// or
+//
+// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q
+// <= f_c * f_w * 2^gamma
+//
+// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies
+//
+// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma
+//
+// or
+//
+// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma)
+//
+// The choice of (alpha,gamma) determines the size of the table and the form of
+// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well
+// in practice:
+//
+// The idea is to cut the number c * w = f * 2^e into two parts, which can be
+// processed independently: An integral part p1, and a fractional part p2:
+//
+// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e
+// = (f div 2^-e) + (f mod 2^-e) * 2^e
+// = p1 + p2 * 2^e
+//
+// The conversion of p1 into decimal form requires a series of divisions and
+// modulos by (a power of) 10. These operations are faster for 32-bit than for
+// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be
+// achieved by choosing
+//
+// -e >= 32 or e <= -32 := gamma
+//
+// In order to convert the fractional part
+//
+// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...
+//
+// into decimal form, the fraction is repeatedly multiplied by 10 and the digits
+// d[-i] are extracted in order:
+//
+// (10 * p2) div 2^-e = d[-1]
+// (10 * p2) mod 2^-e = d[-2] / 10^1 + ...
+//
+// The multiplication by 10 must not overflow. It is sufficient to choose
+//
+// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.
+//
+// Since p2 = f mod 2^-e < 2^-e,
+//
+// -e <= 60 or e >= -60 := alpha
+
+constexpr int kAlpha = -60;
+constexpr int kGamma = -32;
+
+struct cached_power // c = f * 2^e ~= 10^k
+{
+ std::uint64_t f;
+ int e;
+ int k;
+};
+
+/*!
+For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
+power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
+satisfies (Definition 3.2 from [1])
+
+ alpha <= e_c + e + q <= gamma.
+*/
+inline cached_power get_cached_power_for_binary_exponent(int e)
+{
+ // Now
+ //
+ // alpha <= e_c + e + q <= gamma (1)
+ // ==> f_c * 2^alpha <= c * 2^e * 2^q
+ //
+ // and since the c's are normalized, 2^(q-1) <= f_c,
+ //
+ // ==> 2^(q - 1 + alpha) <= c * 2^(e + q)
+ // ==> 2^(alpha - e - 1) <= c
+ //
+ // If c were an exact power of ten, i.e. c = 10^k, one may determine k as
+ //
+ // k = ceil( log_10( 2^(alpha - e - 1) ) )
+ // = ceil( (alpha - e - 1) * log_10(2) )
+ //
+ // From the paper:
+ // "In theory the result of the procedure could be wrong since c is rounded,
+ // and the computation itself is approximated [...]. In practice, however,
+ // this simple function is sufficient."
+ //
+ // For IEEE double precision floating-point numbers converted into
+ // normalized diyfp's w = f * 2^e, with q = 64,
+ //
+ // e >= -1022 (min IEEE exponent)
+ // -52 (p - 1)
+ // -52 (p - 1, possibly normalize denormal IEEE numbers)
+ // -11 (normalize the diyfp)
+ // = -1137
+ //
+ // and
+ //
+ // e <= +1023 (max IEEE exponent)
+ // -52 (p - 1)
+ // -11 (normalize the diyfp)
+ // = 960
+ //
+ // This binary exponent range [-1137,960] results in a decimal exponent
+ // range [-307,324]. One does not need to store a cached power for each
+ // k in this range. For each such k it suffices to find a cached power
+ // such that the exponent of the product lies in [alpha,gamma].
+ // This implies that the difference of the decimal exponents of adjacent
+ // table entries must be less than or equal to
+ //
+ // floor( (gamma - alpha) * log_10(2) ) = 8.
+ //
+ // (A smaller distance gamma-alpha would require a larger table.)
+
+ // NB:
+ // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.
+
+ constexpr int kCachedPowersMinDecExp = -300;
+ constexpr int kCachedPowersDecStep = 8;
+
+ static constexpr std::array<cached_power, 79> kCachedPowers =
+ {
+ {
+ { 0xAB70FE17C79AC6CA, -1060, -300 },
+ { 0xFF77B1FCBEBCDC4F, -1034, -292 },
+ { 0xBE5691EF416BD60C, -1007, -284 },
+ { 0x8DD01FAD907FFC3C, -980, -276 },
+ { 0xD3515C2831559A83, -954, -268 },
+ { 0x9D71AC8FADA6C9B5, -927, -260 },
+ { 0xEA9C227723EE8BCB, -901, -252 },
+ { 0xAECC49914078536D, -874, -244 },
+ { 0x823C12795DB6CE57, -847, -236 },
+ { 0xC21094364DFB5637, -821, -228 },
+ { 0x9096EA6F3848984F, -794, -220 },
+ { 0xD77485CB25823AC7, -768, -212 },
+ { 0xA086CFCD97BF97F4, -741, -204 },
+ { 0xEF340A98172AACE5, -715, -196 },
+ { 0xB23867FB2A35B28E, -688, -188 },
+ { 0x84C8D4DFD2C63F3B, -661, -180 },
+ { 0xC5DD44271AD3CDBA, -635, -172 },
+ { 0x936B9FCEBB25C996, -608, -164 },
+ { 0xDBAC6C247D62A584, -582, -156 },
+ { 0xA3AB66580D5FDAF6, -555, -148 },
+ { 0xF3E2F893DEC3F126, -529, -140 },
+ { 0xB5B5ADA8AAFF80B8, -502, -132 },
+ { 0x87625F056C7C4A8B, -475, -124 },
+ { 0xC9BCFF6034C13053, -449, -116 },
+ { 0x964E858C91BA2655, -422, -108 },
+ { 0xDFF9772470297EBD, -396, -100 },
+ { 0xA6DFBD9FB8E5B88F, -369, -92 },
+ { 0xF8A95FCF88747D94, -343, -84 },
+ { 0xB94470938FA89BCF, -316, -76 },
+ { 0x8A08F0F8BF0F156B, -289, -68 },
+ { 0xCDB02555653131B6, -263, -60 },
+ { 0x993FE2C6D07B7FAC, -236, -52 },
+ { 0xE45C10C42A2B3B06, -210, -44 },
+ { 0xAA242499697392D3, -183, -36 },
+ { 0xFD87B5F28300CA0E, -157, -28 },
+ { 0xBCE5086492111AEB, -130, -20 },
+ { 0x8CBCCC096F5088CC, -103, -12 },
+ { 0xD1B71758E219652C, -77, -4 },
+ { 0x9C40000000000000, -50, 4 },
+ { 0xE8D4A51000000000, -24, 12 },
+ { 0xAD78EBC5AC620000, 3, 20 },
+ { 0x813F3978F8940984, 30, 28 },
+ { 0xC097CE7BC90715B3, 56, 36 },
+ { 0x8F7E32CE7BEA5C70, 83, 44 },
+ { 0xD5D238A4ABE98068, 109, 52 },
+ { 0x9F4F2726179A2245, 136, 60 },
+ { 0xED63A231D4C4FB27, 162, 68 },
+ { 0xB0DE65388CC8ADA8, 189, 76 },
+ { 0x83C7088E1AAB65DB, 216, 84 },
+ { 0xC45D1DF942711D9A, 242, 92 },
+ { 0x924D692CA61BE758, 269, 100 },
+ { 0xDA01EE641A708DEA, 295, 108 },
+ { 0xA26DA3999AEF774A, 322, 116 },
+ { 0xF209787BB47D6B85, 348, 124 },
+ { 0xB454E4A179DD1877, 375, 132 },
+ { 0x865B86925B9BC5C2, 402, 140 },
+ { 0xC83553C5C8965D3D, 428, 148 },
+ { 0x952AB45CFA97A0B3, 455, 156 },
+ { 0xDE469FBD99A05FE3, 481, 164 },
+ { 0xA59BC234DB398C25, 508, 172 },
+ { 0xF6C69A72A3989F5C, 534, 180 },
+ { 0xB7DCBF5354E9BECE, 561, 188 },
+ { 0x88FCF317F22241E2, 588, 196 },
+ { 0xCC20CE9BD35C78A5, 614, 204 },
+ { 0x98165AF37B2153DF, 641, 212 },
+ { 0xE2A0B5DC971F303A, 667, 220 },
+ { 0xA8D9D1535CE3B396, 694, 228 },
+ { 0xFB9B7CD9A4A7443C, 720, 236 },
+ { 0xBB764C4CA7A44410, 747, 244 },
+ { 0x8BAB8EEFB6409C1A, 774, 252 },
+ { 0xD01FEF10A657842C, 800, 260 },
+ { 0x9B10A4E5E9913129, 827, 268 },
+ { 0xE7109BFBA19C0C9D, 853, 276 },
+ { 0xAC2820D9623BF429, 880, 284 },
+ { 0x80444B5E7AA7CF85, 907, 292 },
+ { 0xBF21E44003ACDD2D, 933, 300 },
+ { 0x8E679C2F5E44FF8F, 960, 308 },
+ { 0xD433179D9C8CB841, 986, 316 },
+ { 0x9E19DB92B4E31BA9, 1013, 324 },
+ }
+ };
+
+ // This computation gives exactly the same results for k as
+ // k = ceil((kAlpha - e - 1) * 0.30102999566398114)
+ // for |e| <= 1500, but doesn't require floating-point operations.
+ // NB: log_10(2) ~= 78913 / 2^18
+ JSON_ASSERT(e >= -1500);
+ JSON_ASSERT(e <= 1500);
+ const int f = kAlpha - e - 1;
+ const int k = ((f * 78913) / (1 << 18)) + static_cast<int>(f > 0);
+
+ const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;
+ JSON_ASSERT(index >= 0);
+ JSON_ASSERT(static_cast<std::size_t>(index) < kCachedPowers.size());
+
+ const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];
+ JSON_ASSERT(kAlpha <= cached.e + e + 64);
+ JSON_ASSERT(kGamma >= cached.e + e + 64);
+
+ return cached;
+}
+
+/*!
+For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
+For n == 0, returns 1 and sets pow10 := 1.
+*/
+inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10)
+{
+ // LCOV_EXCL_START
+ if (n >= 1000000000)
+ {
+ pow10 = 1000000000;
+ return 10;
+ }
+ // LCOV_EXCL_STOP
+ if (n >= 100000000)
+ {
+ pow10 = 100000000;
+ return 9;
+ }
+ if (n >= 10000000)
+ {
+ pow10 = 10000000;
+ return 8;
+ }
+ if (n >= 1000000)
+ {
+ pow10 = 1000000;
+ return 7;
+ }
+ if (n >= 100000)
+ {
+ pow10 = 100000;
+ return 6;
+ }
+ if (n >= 10000)
+ {
+ pow10 = 10000;
+ return 5;
+ }
+ if (n >= 1000)
+ {
+ pow10 = 1000;
+ return 4;
+ }
+ if (n >= 100)
+ {
+ pow10 = 100;
+ return 3;
+ }
+ if (n >= 10)
+ {
+ pow10 = 10;
+ return 2;
+ }
+
+ pow10 = 1;
+ return 1;
+}
+
+inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta,
+ std::uint64_t rest, std::uint64_t ten_k)
+{
+ JSON_ASSERT(len >= 1);
+ JSON_ASSERT(dist <= delta);
+ JSON_ASSERT(rest <= delta);
+ JSON_ASSERT(ten_k > 0);
+
+ // <--------------------------- delta ---->
+ // <---- dist --------->
+ // --------------[------------------+-------------------]--------------
+ // M- w M+
+ //
+ // ten_k
+ // <------>
+ // <---- rest ---->
+ // --------------[------------------+----+--------------]--------------
+ // w V
+ // = buf * 10^k
+ //
+ // ten_k represents a unit-in-the-last-place in the decimal representation
+ // stored in buf.
+ // Decrement buf by ten_k while this takes buf closer to w.
+
+ // The tests are written in this order to avoid overflow in unsigned
+ // integer arithmetic.
+
+ while (rest < dist
+ && delta - rest >= ten_k
+ && (rest + ten_k < dist || dist - rest > rest + ten_k - dist))
+ {
+ JSON_ASSERT(buf[len - 1] != '0');
+ buf[len - 1]--;
+ rest += ten_k;
+ }
+}
+
+/*!
+Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
+M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
+*/
+inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,
+ diyfp M_minus, diyfp w, diyfp M_plus)
+{
+ static_assert(kAlpha >= -60, "internal error");
+ static_assert(kGamma <= -32, "internal error");
+
+ // Generates the digits (and the exponent) of a decimal floating-point
+ // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's
+ // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.
+ //
+ // <--------------------------- delta ---->
+ // <---- dist --------->
+ // --------------[------------------+-------------------]--------------
+ // M- w M+
+ //
+ // Grisu2 generates the digits of M+ from left to right and stops as soon as
+ // V is in [M-,M+].
+
+ JSON_ASSERT(M_plus.e >= kAlpha);
+ JSON_ASSERT(M_plus.e <= kGamma);
+
+ std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)
+ std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e)
+
+ // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):
+ //
+ // M+ = f * 2^e
+ // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e
+ // = ((p1 ) * 2^-e + (p2 )) * 2^e
+ // = p1 + p2 * 2^e
+
+ const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);
+
+ auto p1 = static_cast<std::uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
+ std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e
+
+ // 1)
+ //
+ // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
+
+ JSON_ASSERT(p1 > 0);
+
+ std::uint32_t pow10{};
+ const int k = find_largest_pow10(p1, pow10);
+
+ // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
+ //
+ // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))
+ // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1))
+ //
+ // M+ = p1 + p2 * 2^e
+ // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e
+ // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e
+ // = d[k-1] * 10^(k-1) + ( rest) * 2^e
+ //
+ // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)
+ //
+ // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]
+ //
+ // but stop as soon as
+ //
+ // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e
+
+ int n = k;
+ while (n > 0)
+ {
+ // Invariants:
+ // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k)
+ // pow10 = 10^(n-1) <= p1 < 10^n
+ //
+ const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1)
+ const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1)
+ //
+ // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
+ // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
+ //
+ JSON_ASSERT(d <= 9);
+ buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+ //
+ // M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
+ //
+ p1 = r;
+ n--;
+ //
+ // M+ = buffer * 10^n + (p1 + p2 * 2^e)
+ // pow10 = 10^n
+ //
+
+ // Now check if enough digits have been generated.
+ // Compute
+ //
+ // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e
+ //
+ // Note:
+ // Since rest and delta share the same exponent e, it suffices to
+ // compare the significands.
+ const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;
+ if (rest <= delta)
+ {
+ // V = buffer * 10^n, with M- <= V <= M+.
+
+ decimal_exponent += n;
+
+ // We may now just stop. But instead look if the buffer could be
+ // decremented to bring V closer to w.
+ //
+ // pow10 = 10^n is now 1 ulp in the decimal representation V.
+ // The rounding procedure works with diyfp's with an implicit
+ // exponent of e.
+ //
+ // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e
+ //
+ const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;
+ grisu2_round(buffer, length, dist, delta, rest, ten_n);
+
+ return;
+ }
+
+ pow10 /= 10;
+ //
+ // pow10 = 10^(n-1) <= p1 < 10^n
+ // Invariants restored.
+ }
+
+ // 2)
+ //
+ // The digits of the integral part have been generated:
+ //
+ // M+ = d[k-1]...d[1]d[0] + p2 * 2^e
+ // = buffer + p2 * 2^e
+ //
+ // Now generate the digits of the fractional part p2 * 2^e.
+ //
+ // Note:
+ // No decimal point is generated: the exponent is adjusted instead.
+ //
+ // p2 actually represents the fraction
+ //
+ // p2 * 2^e
+ // = p2 / 2^-e
+ // = d[-1] / 10^1 + d[-2] / 10^2 + ...
+ //
+ // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)
+ //
+ // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m
+ // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)
+ //
+ // using
+ //
+ // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)
+ // = ( d) * 2^-e + ( r)
+ //
+ // or
+ // 10^m * p2 * 2^e = d + r * 2^e
+ //
+ // i.e.
+ //
+ // M+ = buffer + p2 * 2^e
+ // = buffer + 10^-m * (d + r * 2^e)
+ // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e
+ //
+ // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
+
+ JSON_ASSERT(p2 > delta);
+
+ int m = 0;
+ for (;;)
+ {
+ // Invariant:
+ // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e
+ // = buffer * 10^-m + 10^-m * (p2 ) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e
+ //
+ JSON_ASSERT(p2 <= (std::numeric_limits<std::uint64_t>::max)() / 10);
+ p2 *= 10;
+ const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e
+ const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
+ //
+ // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e
+ // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
+ // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
+ //
+ JSON_ASSERT(d <= 9);
+ buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+ //
+ // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
+ //
+ p2 = r;
+ m++;
+ //
+ // M+ = buffer * 10^-m + 10^-m * p2 * 2^e
+ // Invariant restored.
+
+ // Check if enough digits have been generated.
+ //
+ // 10^-m * p2 * 2^e <= delta * 2^e
+ // p2 * 2^e <= 10^m * delta * 2^e
+ // p2 <= 10^m * delta
+ delta *= 10;
+ dist *= 10;
+ if (p2 <= delta)
+ {
+ break;
+ }
+ }
+
+ // V = buffer * 10^-m, with M- <= V <= M+.
+
+ decimal_exponent -= m;
+
+ // 1 ulp in the decimal representation is now 10^-m.
+ // Since delta and dist are now scaled by 10^m, we need to do the
+ // same with ulp in order to keep the units in sync.
+ //
+ // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e
+ //
+ const std::uint64_t ten_m = one.f;
+ grisu2_round(buffer, length, dist, delta, p2, ten_m);
+
+ // By construction this algorithm generates the shortest possible decimal
+ // number (Loitsch, Theorem 6.2) which rounds back to w.
+ // For an input number of precision p, at least
+ //
+ // N = 1 + ceil(p * log_10(2))
+ //
+ // decimal digits are sufficient to identify all binary floating-point
+ // numbers (Matula, "In-and-Out conversions").
+ // This implies that the algorithm does not produce more than N decimal
+ // digits.
+ //
+ // N = 17 for p = 53 (IEEE double precision)
+ // N = 9 for p = 24 (IEEE single precision)
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+JSON_HEDLEY_NON_NULL(1)
+inline void grisu2(char* buf, int& len, int& decimal_exponent,
+ diyfp m_minus, diyfp v, diyfp m_plus)
+{
+ JSON_ASSERT(m_plus.e == m_minus.e);
+ JSON_ASSERT(m_plus.e == v.e);
+
+ // --------(-----------------------+-----------------------)-------- (A)
+ // m- v m+
+ //
+ // --------------------(-----------+-----------------------)-------- (B)
+ // m- v m+
+ //
+ // First scale v (and m- and m+) such that the exponent is in the range
+ // [alpha, gamma].
+
+ const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);
+
+ const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k
+
+ // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]
+ const diyfp w = diyfp::mul(v, c_minus_k);
+ const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);
+ const diyfp w_plus = diyfp::mul(m_plus, c_minus_k);
+
+ // ----(---+---)---------------(---+---)---------------(---+---)----
+ // w- w w+
+ // = c*m- = c*v = c*m+
+ //
+ // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and
+ // w+ are now off by a small amount.
+ // In fact:
+ //
+ // w - v * 10^k < 1 ulp
+ //
+ // To account for this inaccuracy, add resp. subtract 1 ulp.
+ //
+ // --------+---[---------------(---+---)---------------]---+--------
+ // w- M- w M+ w+
+ //
+ // Now any number in [M-, M+] (bounds included) will round to w when input,
+ // regardless of how the input rounding algorithm breaks ties.
+ //
+ // And digit_gen generates the shortest possible such number in [M-, M+].
+ // Note that this does not mean that Grisu2 always generates the shortest
+ // possible number in the interval (m-, m+).
+ const diyfp M_minus(w_minus.f + 1, w_minus.e);
+ const diyfp M_plus (w_plus.f - 1, w_plus.e );
+
+ decimal_exponent = -cached.k; // = -(-k) = k
+
+ grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+template<typename FloatType>
+JSON_HEDLEY_NON_NULL(1)
+void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)
+{
+ static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
+ "internal error: not enough precision");
+
+ JSON_ASSERT(std::isfinite(value));
+ JSON_ASSERT(value > 0);
+
+ // If the neighbors (and boundaries) of 'value' are always computed for double-precision
+ // numbers, all float's can be recovered using strtod (and strtof). However, the resulting
+ // decimal representations are not exactly "short".
+ //
+ // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)
+ // says "value is converted to a string as if by std::sprintf in the default ("C") locale"
+ // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars'
+ // does.
+ // On the other hand, the documentation for 'std::to_chars' requires that "parsing the
+ // representation using the corresponding std::from_chars function recovers value exactly". That
+ // indicates that single precision floating-point numbers should be recovered using
+ // 'std::strtof'.
+ //
+ // NB: If the neighbors are computed for single-precision numbers, there is a single float
+ // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision
+ // value is off by 1 ulp.
+#if 0 // NOLINT(readability-avoid-unconditional-preprocessor-if)
+ const boundaries w = compute_boundaries(static_cast<double>(value));
+#else
+ const boundaries w = compute_boundaries(value);
+#endif
+
+ grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
+}
+
+/*!
+@brief appends a decimal representation of e to buf
+@return a pointer to the element following the exponent.
+@pre -1000 < e < 1000
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* append_exponent(char* buf, int e)
+{
+ JSON_ASSERT(e > -1000);
+ JSON_ASSERT(e < 1000);
+
+ if (e < 0)
+ {
+ e = -e;
+ *buf++ = '-';
+ }
+ else
+ {
+ *buf++ = '+';
+ }
+
+ auto k = static_cast<std::uint32_t>(e);
+ if (k < 10)
+ {
+ // Always print at least two digits in the exponent.
+ // This is for compatibility with printf("%g").
+ *buf++ = '0';
+ *buf++ = static_cast<char>('0' + k);
+ }
+ else if (k < 100)
+ {
+ *buf++ = static_cast<char>('0' + (k / 10));
+ k %= 10;
+ *buf++ = static_cast<char>('0' + k);
+ }
+ else
+ {
+ *buf++ = static_cast<char>('0' + (k / 100));
+ k %= 100;
+ *buf++ = static_cast<char>('0' + (k / 10));
+ k %= 10;
+ *buf++ = static_cast<char>('0' + k);
+ }
+
+ return buf;
+}
+
+/*!
+@brief prettify v = buf * 10^decimal_exponent
+
+If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
+notation. Otherwise it will be printed in exponential notation.
+
+@pre min_exp < 0
+@pre max_exp > 0
+*/
+JSON_HEDLEY_NON_NULL(1)
+JSON_HEDLEY_RETURNS_NON_NULL
+inline char* format_buffer(char* buf, int len, int decimal_exponent,
+ int min_exp, int max_exp)
+{
+ JSON_ASSERT(min_exp < 0);
+ JSON_ASSERT(max_exp > 0);
+
+ const int k = len;
+ const int n = len + decimal_exponent;
+
+ // v = buf * 10^(n-k)
+ // k is the length of the buffer (number of decimal digits)
+ // n is the position of the decimal point relative to the start of the buffer.
+
+ if (k <= n && n <= max_exp)
+ {
+ // digits[000]
+ // len <= max_exp + 2
+
+ std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));
+ // Make it look like a floating-point number (#362, #378)
+ buf[n + 0] = '.';
+ buf[n + 1] = '0';
+ return buf + (static_cast<size_t>(n) + 2);
+ }
+
+ if (0 < n && n <= max_exp)
+ {
+ // dig.its
+ // len <= max_digits10 + 1
+
+ JSON_ASSERT(k > n);
+
+ std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n, static_cast<size_t>(k) - static_cast<size_t>(n));
+ buf[n] = '.';
+ return buf + (static_cast<size_t>(k) + 1U);
+ }
+
+ if (min_exp < n && n <= 0)
+ {
+ // 0.[000]digits
+ // len <= 2 + (-min_exp - 1) + max_digits10
+
+ std::memmove(buf + (2 + static_cast<size_t>(-n)), buf, static_cast<size_t>(k));
+ buf[0] = '0';
+ buf[1] = '.';
+ std::memset(buf + 2, '0', static_cast<size_t>(-n));
+ return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));
+ }
+
+ if (k == 1)
+ {
+ // dE+123
+ // len <= 1 + 5
+
+ buf += 1;
+ }
+ else
+ {
+ // d.igitsE+123
+ // len <= max_digits10 + 1 + 5
+
+ std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);
+ buf[1] = '.';
+ buf += 1 + static_cast<size_t>(k);
+ }
+
+ *buf++ = 'e';
+ return append_exponent(buf, n - 1);
+}
+
+} // namespace dtoa_impl
+
+/*!
+@brief generates a decimal representation of the floating-point number value in [first, last).
+
+The format of the resulting decimal representation is similar to printf's %g
+format. Returns an iterator pointing past-the-end of the decimal representation.
+
+@note The input number must be finite, i.e. NaN's and Inf's are not supported.
+@note The buffer must be large enough.
+@note The result is NOT null-terminated.
+*/
+template<typename FloatType>
+JSON_HEDLEY_NON_NULL(1, 2)
+JSON_HEDLEY_RETURNS_NON_NULL
+char* to_chars(char* first, const char* last, FloatType value)
+{
+ static_cast<void>(last); // maybe unused - fix warning
+ JSON_ASSERT(std::isfinite(value));
+
+ // Use signbit(value) instead of (value < 0) since signbit works for -0.
+ if (std::signbit(value))
+ {
+ value = -value;
+ *first++ = '-';
+ }
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ if (value == 0) // +-0
+ {
+ *first++ = '0';
+ // Make it look like a floating-point number (#362, #378)
+ *first++ = '.';
+ *first++ = '0';
+ return first;
+ }
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+ JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10);
+
+ // Compute v = buffer * 10^decimal_exponent.
+ // The decimal digits are stored in the buffer, which needs to be interpreted
+ // as an unsigned decimal integer.
+ // len is the length of the buffer, i.e. the number of decimal digits.
+ int len = 0;
+ int decimal_exponent = 0;
+ dtoa_impl::grisu2(first, len, decimal_exponent, value);
+
+ JSON_ASSERT(len <= std::numeric_limits<FloatType>::max_digits10);
+
+ // Format the buffer like printf("%.*g", prec, value)
+ constexpr int kMinExp = -4;
+ // Use digits10 here to increase compatibility with version 2.
+ constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;
+
+ JSON_ASSERT(last - first >= kMaxExp + 2);
+ JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);
+ JSON_ASSERT(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);
+
+ return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
+}
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/exceptions.hpp>
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/cpp_future.hpp>
+
+// #include <nlohmann/detail/output/binary_writer.hpp>
+
+// #include <nlohmann/detail/output/output_adapters.hpp>
+
+// #include <nlohmann/detail/string_concat.hpp>
+
+// #include <nlohmann/detail/value_t.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+namespace detail
+{
+
+///////////////////
+// serialization //
+///////////////////
+
+/// how to treat decoding errors
+enum class error_handler_t
+{
+ strict, ///< throw a type_error exception in case of invalid UTF-8
+ replace, ///< replace invalid UTF-8 sequences with U+FFFD
+ ignore ///< ignore invalid UTF-8 sequences
+};
+
+template<typename BasicJsonType>
+class serializer
+{
+ using string_t = typename BasicJsonType::string_t;
+ using number_float_t = typename BasicJsonType::number_float_t;
+ using number_integer_t = typename BasicJsonType::number_integer_t;
+ using number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using binary_char_t = typename BasicJsonType::binary_t::value_type;
+ static constexpr std::uint8_t UTF8_ACCEPT = 0;
+ static constexpr std::uint8_t UTF8_REJECT = 1;
+
+ public:
+ /*!
+ @param[in] s output stream to serialize to
+ @param[in] ichar indentation character to use
+ @param[in] error_handler_ how to react on decoding errors
+ */
+ serializer(output_adapter_t<char> s, const char ichar,
+ error_handler_t error_handler_ = error_handler_t::strict)
+ : o(std::move(s))
+ , loc(std::localeconv())
+ , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->thousands_sep)))
+ , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits<char>::to_char_type(* (loc->decimal_point)))
+ , indent_char(ichar)
+ , indent_string(512, indent_char)
+ , error_handler(error_handler_)
+ {}
+
+ // delete because of pointer members
+ serializer(const serializer&) = delete;
+ serializer& operator=(const serializer&) = delete;
+ serializer(serializer&&) = delete;
+ serializer& operator=(serializer&&) = delete;
+ ~serializer() = default;
+
+ /*!
+ @brief internal implementation of the serialization function
+
+ This function is called by the public member function dump and organizes
+ the serialization internally. The indentation level is propagated as
+ additional parameter. In case of arrays and objects, the function is
+ called recursively.
+
+ - strings and object keys are escaped using `escape_string()`
+ - integer numbers are converted implicitly via `operator<<`
+ - floating-point numbers are converted to a string using `"%g"` format
+ - binary values are serialized as objects containing the subtype and the
+ byte array
+
+ @param[in] val value to serialize
+ @param[in] pretty_print whether the output shall be pretty-printed
+ @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters
+ in the output are escaped with `\uXXXX` sequences, and the result consists
+ of ASCII characters only.
+ @param[in] indent_step the indent level
+ @param[in] current_indent the current indent level (only used internally)
+ */
+ void dump(const BasicJsonType& val,
+ const bool pretty_print,
+ const bool ensure_ascii,
+ const unsigned int indent_step,
+ const unsigned int current_indent = 0)
+ {
+ switch (val.m_data.m_type)
+ {
+ case value_t::object:
+ {
+ if (val.m_data.m_value.object->empty())
+ {
+ o->write_characters("{}", 2);
+ return;
+ }
+
+ if (pretty_print)
+ {
+ o->write_characters("{\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ // first n-1 elements
+ auto i = val.m_data.m_value.object->cbegin();
+ for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
+ {
+ o->write_characters(indent_string.c_str(), new_indent);
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\": ", 3);
+ dump(i->second, true, ensure_ascii, indent_step, new_indent);
+ o->write_characters(",\n", 2);
+ }
+
+ // last element
+ JSON_ASSERT(i != val.m_data.m_value.object->cend());
+ JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
+ o->write_characters(indent_string.c_str(), new_indent);
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\": ", 3);
+ dump(i->second, true, ensure_ascii, indent_step, new_indent);
+
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character('}');
+ }
+ else
+ {
+ o->write_character('{');
+
+ // first n-1 elements
+ auto i = val.m_data.m_value.object->cbegin();
+ for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i)
+ {
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\":", 2);
+ dump(i->second, false, ensure_ascii, indent_step, current_indent);
+ o->write_character(',');
+ }
+
+ // last element
+ JSON_ASSERT(i != val.m_data.m_value.object->cend());
+ JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend());
+ o->write_character('\"');
+ dump_escaped(i->first, ensure_ascii);
+ o->write_characters("\":", 2);
+ dump(i->second, false, ensure_ascii, indent_step, current_indent);
+
+ o->write_character('}');
+ }
+
+ return;
+ }
+
+ case value_t::array:
+ {
+ if (val.m_data.m_value.array->empty())
+ {
+ o->write_characters("[]", 2);
+ return;
+ }
+
+ if (pretty_print)
+ {
+ o->write_characters("[\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ // first n-1 elements
+ for (auto i = val.m_data.m_value.array->cbegin();
+ i != val.m_data.m_value.array->cend() - 1; ++i)
+ {
+ o->write_characters(indent_string.c_str(), new_indent);
+ dump(*i, true, ensure_ascii, indent_step, new_indent);
+ o->write_characters(",\n", 2);
+ }
+
+ // last element
+ JSON_ASSERT(!val.m_data.m_value.array->empty());
+ o->write_characters(indent_string.c_str(), new_indent);
+ dump(val.m_data.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);
+
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character(']');
+ }
+ else
+ {
+ o->write_character('[');
+
+ // first n-1 elements
+ for (auto i = val.m_data.m_value.array->cbegin();
+ i != val.m_data.m_value.array->cend() - 1; ++i)
+ {
+ dump(*i, false, ensure_ascii, indent_step, current_indent);
+ o->write_character(',');
+ }
+
+ // last element
+ JSON_ASSERT(!val.m_data.m_value.array->empty());
+ dump(val.m_data.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);
+
+ o->write_character(']');
+ }
+
+ return;
+ }
+
+ case value_t::string:
+ {
+ o->write_character('\"');
+ dump_escaped(*val.m_data.m_value.string, ensure_ascii);
+ o->write_character('\"');
+ return;
+ }
+
+ case value_t::binary:
+ {
+ if (pretty_print)
+ {
+ o->write_characters("{\n", 2);
+
+ // variable to hold indentation for recursive calls
+ const auto new_indent = current_indent + indent_step;
+ if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent))
+ {
+ indent_string.resize(indent_string.size() * 2, ' ');
+ }
+
+ o->write_characters(indent_string.c_str(), new_indent);
+
+ o->write_characters("\"bytes\": [", 10);
+
+ if (!val.m_data.m_value.binary->empty())
+ {
+ for (auto i = val.m_data.m_value.binary->cbegin();
+ i != val.m_data.m_value.binary->cend() - 1; ++i)
+ {
+ dump_integer(*i);
+ o->write_characters(", ", 2);
+ }
+ dump_integer(val.m_data.m_value.binary->back());
+ }
+
+ o->write_characters("],\n", 3);
+ o->write_characters(indent_string.c_str(), new_indent);
+
+ o->write_characters("\"subtype\": ", 11);
+ if (val.m_data.m_value.binary->has_subtype())
+ {
+ dump_integer(val.m_data.m_value.binary->subtype());
+ }
+ else
+ {
+ o->write_characters("null", 4);
+ }
+ o->write_character('\n');
+ o->write_characters(indent_string.c_str(), current_indent);
+ o->write_character('}');
+ }
+ else
+ {
+ o->write_characters("{\"bytes\":[", 10);
+
+ if (!val.m_data.m_value.binary->empty())
+ {
+ for (auto i = val.m_data.m_value.binary->cbegin();
+ i != val.m_data.m_value.binary->cend() - 1; ++i)
+ {
+ dump_integer(*i);
+ o->write_character(',');
+ }
+ dump_integer(val.m_data.m_value.binary->back());
+ }
+
+ o->write_characters("],\"subtype\":", 12);
+ if (val.m_data.m_value.binary->has_subtype())
+ {
+ dump_integer(val.m_data.m_value.binary->subtype());
+ o->write_character('}');
+ }
+ else
+ {
+ o->write_characters("null}", 5);
+ }
+ }
+ return;
+ }
+
+ case value_t::boolean:
+ {
+ if (val.m_data.m_value.boolean)
+ {
+ o->write_characters("true", 4);
+ }
+ else
+ {
+ o->write_characters("false", 5);
+ }
+ return;
+ }
+
+ case value_t::number_integer:
+ {
+ dump_integer(val.m_data.m_value.number_integer);
+ return;
+ }
+
+ case value_t::number_unsigned:
+ {
+ dump_integer(val.m_data.m_value.number_unsigned);
+ return;
+ }
+
+ case value_t::number_float:
+ {
+ dump_float(val.m_data.m_value.number_float);
+ return;
+ }
+
+ case value_t::discarded:
+ {
+ o->write_characters("<discarded>", 11);
+ return;
+ }
+
+ case value_t::null:
+ {
+ o->write_characters("null", 4);
+ return;
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ }
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /*!
+ @brief dump escaped string
+
+ Escape a string by replacing certain special characters by a sequence of an
+ escape character (backslash) and another character and other control
+ characters by a sequence of "\u" followed by a four-digit hex
+ representation. The escaped string is written to output stream @a o.
+
+ @param[in] s the string to escape
+ @param[in] ensure_ascii whether to escape non-ASCII characters with
+ \uXXXX sequences
+
+ @complexity Linear in the length of string @a s.
+ */
+ void dump_escaped(const string_t& s, const bool ensure_ascii)
+ {
+ std::uint32_t codepoint{};
+ std::uint8_t state = UTF8_ACCEPT;
+ std::size_t bytes = 0; // number of bytes written to string_buffer
+
+ // number of bytes written at the point of the last valid byte
+ std::size_t bytes_after_last_accept = 0;
+ std::size_t undumped_chars = 0;
+
+ for (std::size_t i = 0; i < s.size(); ++i)
+ {
+ const auto byte = static_cast<std::uint8_t>(s[i]);
+
+ switch (decode(state, codepoint, byte))
+ {
+ case UTF8_ACCEPT: // decode found a new code point
+ {
+ switch (codepoint)
+ {
+ case 0x08: // backspace
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'b';
+ break;
+ }
+
+ case 0x09: // horizontal tab
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 't';
+ break;
+ }
+
+ case 0x0A: // newline
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'n';
+ break;
+ }
+
+ case 0x0C: // formfeed
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'f';
+ break;
+ }
+
+ case 0x0D: // carriage return
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'r';
+ break;
+ }
+
+ case 0x22: // quotation mark
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = '\"';
+ break;
+ }
+
+ case 0x5C: // reverse solidus
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = '\\';
+ break;
+ }
+
+ default:
+ {
+ // escape control characters (0x00..0x1F) or, if
+ // ensure_ascii parameter is used, non-ASCII characters
+ if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F)))
+ {
+ if (codepoint <= 0xFFFF)
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x",
+ static_cast<std::uint16_t>(codepoint)));
+ bytes += 6;
+ }
+ else
+ {
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ static_cast<void>((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x",
+ static_cast<std::uint16_t>(0xD7C0u + (codepoint >> 10u)),
+ static_cast<std::uint16_t>(0xDC00u + (codepoint & 0x3FFu))));
+ bytes += 12;
+ }
+ }
+ else
+ {
+ // copy byte to buffer (all previous bytes
+ // been copied have in default case above)
+ string_buffer[bytes++] = s[i];
+ }
+ break;
+ }
+ }
+
+ // write buffer and reset index; there must be 13 bytes
+ // left, as this is the maximal number of bytes to be
+ // written ("\uxxxx\uxxxx\0") for one code point
+ if (string_buffer.size() - bytes < 13)
+ {
+ o->write_characters(string_buffer.data(), bytes);
+ bytes = 0;
+ }
+
+ // remember the byte position of this accept
+ bytes_after_last_accept = bytes;
+ undumped_chars = 0;
+ break;
+ }
+
+ case UTF8_REJECT: // decode found invalid UTF-8 byte
+ {
+ switch (error_handler)
+ {
+ case error_handler_t::strict:
+ {
+ JSON_THROW(type_error::create(316, concat("invalid UTF-8 byte at index ", std::to_string(i), ": 0x", hex_bytes(byte | 0)), nullptr));
+ }
+
+ case error_handler_t::ignore:
+ case error_handler_t::replace:
+ {
+ // in case we saw this character the first time, we
+ // would like to read it again, because the byte
+ // may be OK for itself, but just not OK for the
+ // previous sequence
+ if (undumped_chars > 0)
+ {
+ --i;
+ }
+
+ // reset length buffer to the last accepted index;
+ // thus removing/ignoring the invalid characters
+ bytes = bytes_after_last_accept;
+
+ if (error_handler == error_handler_t::replace)
+ {
+ // add a replacement character
+ if (ensure_ascii)
+ {
+ string_buffer[bytes++] = '\\';
+ string_buffer[bytes++] = 'u';
+ string_buffer[bytes++] = 'f';
+ string_buffer[bytes++] = 'f';
+ string_buffer[bytes++] = 'f';
+ string_buffer[bytes++] = 'd';
+ }
+ else
+ {
+ string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xEF');
+ string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBF');
+ string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\xBD');
+ }
+
+ // write buffer and reset index; there must be 13 bytes
+ // left, as this is the maximal number of bytes to be
+ // written ("\uxxxx\uxxxx\0") for one code point
+ if (string_buffer.size() - bytes < 13)
+ {
+ o->write_characters(string_buffer.data(), bytes);
+ bytes = 0;
+ }
+
+ bytes_after_last_accept = bytes;
+ }
+
+ undumped_chars = 0;
+
+ // continue processing the string
+ state = UTF8_ACCEPT;
+ break;
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ break;
+ }
+
+ default: // decode found yet incomplete multi-byte code point
+ {
+ if (!ensure_ascii)
+ {
+ // code point will not be escaped - copy byte to buffer
+ string_buffer[bytes++] = s[i];
+ }
+ ++undumped_chars;
+ break;
+ }
+ }
+ }
+
+ // we finished processing the string
+ if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT))
+ {
+ // write buffer
+ if (bytes > 0)
+ {
+ o->write_characters(string_buffer.data(), bytes);
+ }
+ }
+ else
+ {
+ // we finish reading, but do not accept: string was incomplete
+ switch (error_handler)
+ {
+ case error_handler_t::strict:
+ {
+ JSON_THROW(type_error::create(316, concat("incomplete UTF-8 string; last byte: 0x", hex_bytes(static_cast<std::uint8_t>(s.back() | 0))), nullptr));
+ }
+
+ case error_handler_t::ignore:
+ {
+ // write all accepted bytes
+ o->write_characters(string_buffer.data(), bytes_after_last_accept);
+ break;
+ }
+
+ case error_handler_t::replace:
+ {
+ // write all accepted bytes
+ o->write_characters(string_buffer.data(), bytes_after_last_accept);
+ // add a replacement character
+ if (ensure_ascii)
+ {
+ o->write_characters("\\ufffd", 6);
+ }
+ else
+ {
+ o->write_characters("\xEF\xBF\xBD", 3);
+ }
+ break;
+ }
+
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ }
+ }
+
+ private:
+ /*!
+ @brief count digits
+
+ Count the number of decimal (base 10) digits for an input unsigned integer.
+
+ @param[in] x unsigned integer number to count its digits
+ @return number of decimal digits
+ */
+ unsigned int count_digits(number_unsigned_t x) noexcept
+ {
+ unsigned int n_digits = 1;
+ for (;;)
+ {
+ if (x < 10)
+ {
+ return n_digits;
+ }
+ if (x < 100)
+ {
+ return n_digits + 1;
+ }
+ if (x < 1000)
+ {
+ return n_digits + 2;
+ }
+ if (x < 10000)
+ {
+ return n_digits + 3;
+ }
+ x = x / 10000u;
+ n_digits += 4;
+ }
+ }
+
+ /*!
+ * @brief convert a byte to a uppercase hex representation
+ * @param[in] byte byte to represent
+ * @return representation ("00".."FF")
+ */
+ static std::string hex_bytes(std::uint8_t byte)
+ {
+ std::string result = "FF";
+ constexpr const char* nibble_to_hex = "0123456789ABCDEF";
+ result[0] = nibble_to_hex[byte / 16];
+ result[1] = nibble_to_hex[byte % 16];
+ return result;
+ }
+
+ // templates to avoid warnings about useless casts
+ template <typename NumberType, enable_if_t<std::is_signed<NumberType>::value, int> = 0>
+ bool is_negative_number(NumberType x)
+ {
+ return x < 0;
+ }
+
+ template < typename NumberType, enable_if_t <std::is_unsigned<NumberType>::value, int > = 0 >
+ bool is_negative_number(NumberType /*unused*/)
+ {
+ return false;
+ }
+
+ /*!
+ @brief dump an integer
+
+ Dump a given integer to output stream @a o. Works internally with
+ @a number_buffer.
+
+ @param[in] x integer number (signed or unsigned) to dump
+ @tparam NumberType either @a number_integer_t or @a number_unsigned_t
+ */
+ template < typename NumberType, detail::enable_if_t <
+ std::is_integral<NumberType>::value ||
+ std::is_same<NumberType, number_unsigned_t>::value ||
+ std::is_same<NumberType, number_integer_t>::value ||
+ std::is_same<NumberType, binary_char_t>::value,
+ int > = 0 >
+ void dump_integer(NumberType x)
+ {
+ static constexpr std::array<std::array<char, 2>, 100> digits_to_99
+ {
+ {
+ {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}},
+ {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}},
+ {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}},
+ {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}},
+ {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}},
+ {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}},
+ {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}},
+ {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}},
+ {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}},
+ {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}},
+ }
+ };
+
+ // special case for "0"
+ if (x == 0)
+ {
+ o->write_character('0');
+ return;
+ }
+
+ // use a pointer to fill the buffer
+ auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+
+ number_unsigned_t abs_value;
+
+ unsigned int n_chars{};
+
+ if (is_negative_number(x))
+ {
+ *buffer_ptr = '-';
+ abs_value = remove_sign(static_cast<number_integer_t>(x));
+
+ // account one more byte for the minus sign
+ n_chars = 1 + count_digits(abs_value);
+ }
+ else
+ {
+ abs_value = static_cast<number_unsigned_t>(x);
+ n_chars = count_digits(abs_value);
+ }
+
+ // spare 1 byte for '\0'
+ JSON_ASSERT(n_chars < number_buffer.size() - 1);
+
+ // jump to the end to generate the string from backward,
+ // so we later avoid reversing the result
+ buffer_ptr += n_chars;
+
+ // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu
+ // See: https://www.youtube.com/watch?v=o4-CwDo2zpg
+ while (abs_value >= 100)
+ {
+ const auto digits_index = static_cast<unsigned>((abs_value % 100));
+ abs_value /= 100;
+ *(--buffer_ptr) = digits_to_99[digits_index][1];
+ *(--buffer_ptr) = digits_to_99[digits_index][0];
+ }
+
+ if (abs_value >= 10)
+ {
+ const auto digits_index = static_cast<unsigned>(abs_value);
+ *(--buffer_ptr) = digits_to_99[digits_index][1];
+ *(--buffer_ptr) = digits_to_99[digits_index][0];
+ }
+ else
+ {
+ *(--buffer_ptr) = static_cast<char>('0' + abs_value);
+ }
+
+ o->write_characters(number_buffer.data(), n_chars);
+ }
+
+ /*!
+ @brief dump a floating-point number
+
+ Dump a given floating-point number to output stream @a o. Works internally
+ with @a number_buffer.
+
+ @param[in] x floating-point number to dump
+ */
+ void dump_float(number_float_t x)
+ {
+ // NaN / inf
+ if (!std::isfinite(x))
+ {
+ o->write_characters("null", 4);
+ return;
+ }
+
+ // If number_float_t is an IEEE-754 single or double precision number,
+ // use the Grisu2 algorithm to produce short numbers which are
+ // guaranteed to round-trip, using strtof and strtod, resp.
+ //
+ // NB: The test below works if <long double> == <double>.
+ static constexpr bool is_ieee_single_or_double
+ = (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 24 && std::numeric_limits<number_float_t>::max_exponent == 128) ||
+ (std::numeric_limits<number_float_t>::is_iec559 && std::numeric_limits<number_float_t>::digits == 53 && std::numeric_limits<number_float_t>::max_exponent == 1024);
+
+ dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());
+ }
+
+ void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)
+ {
+ auto* begin = number_buffer.data();
+ auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);
+
+ o->write_characters(begin, static_cast<size_t>(end - begin));
+ }
+
+ void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
+ {
+ // get number of digits for a float -> text -> float round-trip
+ static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;
+
+ // the actual conversion
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
+ std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x);
+
+ // negative value indicates an error
+ JSON_ASSERT(len > 0);
+ // check if buffer was large enough
+ JSON_ASSERT(static_cast<std::size_t>(len) < number_buffer.size());
+
+ // erase thousands separator
+ if (thousands_sep != '\0')
+ {
+ // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081
+ const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep);
+ std::fill(end, number_buffer.end(), '\0');
+ JSON_ASSERT((end - number_buffer.begin()) <= len);
+ len = (end - number_buffer.begin());
+ }
+
+ // convert decimal point to '.'
+ if (decimal_point != '\0' && decimal_point != '.')
+ {
+ // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081
+ const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);
+ if (dec_pos != number_buffer.end())
+ {
+ *dec_pos = '.';
+ }
+ }
+
+ o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));
+
+ // determine if we need to append ".0"
+ const bool value_is_int_like =
+ std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,
+ [](char c)
+ {
+ return c == '.' || c == 'e';
+ });
+
+ if (value_is_int_like)
+ {
+ o->write_characters(".0", 2);
+ }
+ }
+
+ /*!
+ @brief check whether a string is UTF-8 encoded
+
+ The function checks each byte of a string whether it is UTF-8 encoded. The
+ result of the check is stored in the @a state parameter. The function must
+ be called initially with state 0 (accept). State 1 means the string must
+ be rejected, because the current byte is not allowed. If the string is
+ completely processed, but the state is non-zero, the string ended
+ prematurely; that is, the last byte indicated more bytes should have
+ followed.
+
+ @param[in,out] state the state of the decoding
+ @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT)
+ @param[in] byte next byte to decode
+ @return new state
+
+ @note The function has been edited: a std::array is used.
+
+ @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
+ */
+ static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept
+ {
+ static const std::array<std::uint8_t, 400> utf8d =
+ {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF
+ 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF
+ 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF
+ 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF
+ 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
+ 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
+ 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
+ 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8
+ }
+ };
+
+ JSON_ASSERT(byte < utf8d.size());
+ const std::uint8_t type = utf8d[byte];
+
+ codep = (state != UTF8_ACCEPT)
+ ? (byte & 0x3fu) | (codep << 6u)
+ : (0xFFu >> type) & (byte);
+
+ const std::size_t index = 256u + (static_cast<size_t>(state) * 16u) + static_cast<size_t>(type);
+ JSON_ASSERT(index < utf8d.size());
+ state = utf8d[index];
+ return state;
+ }
+
+ /*
+ * Overload to make the compiler happy while it is instantiating
+ * dump_integer for number_unsigned_t.
+ * Must never be called.
+ */
+ number_unsigned_t remove_sign(number_unsigned_t x)
+ {
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ return x; // LCOV_EXCL_LINE
+ }
+
+ /*
+ * Helper function for dump_integer
+ *
+ * This function takes a negative signed integer and returns its absolute
+ * value as unsigned integer. The plus/minus shuffling is necessary as we can
+ * not directly remove the sign of an arbitrary signed integer as the
+ * absolute values of INT_MIN and INT_MAX are usually not the same. See
+ * #1708 for details.
+ */
+ number_unsigned_t remove_sign(number_integer_t x) noexcept
+ {
+ JSON_ASSERT(x < 0 && x < (std::numeric_limits<number_integer_t>::max)()); // NOLINT(misc-redundant-expression)
+ return static_cast<number_unsigned_t>(-(x + 1)) + 1;
+ }
+
+ private:
+ /// the output of the serializer
+ output_adapter_t<char> o = nullptr;
+
+ /// a (hopefully) large enough character buffer
+ std::array<char, 64> number_buffer{{}};
+
+ /// the locale
+ const std::lconv* loc = nullptr;
+ /// the locale's thousand separator character
+ const char thousands_sep = '\0';
+ /// the locale's decimal point character
+ const char decimal_point = '\0';
+
+ /// string buffer
+ std::array<char, 512> string_buffer{{}};
+
+ /// the indentation character
+ const char indent_char;
+ /// the indentation string
+ string_t indent_string;
+
+ /// error_handler how to react on decoding errors
+ const error_handler_t error_handler;
+};
+
+} // namespace detail
+NLOHMANN_JSON_NAMESPACE_END
+
+// #include <nlohmann/detail/value_t.hpp>
+
+// #include <nlohmann/json_fwd.hpp>
+
+// #include <nlohmann/ordered_map.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#include <functional> // equal_to, less
+#include <initializer_list> // initializer_list
+#include <iterator> // input_iterator_tag, iterator_traits
+#include <memory> // allocator
+#include <stdexcept> // for out_of_range
+#include <type_traits> // enable_if, is_convertible
+#include <utility> // pair
+#include <vector> // vector
+
+// #include <nlohmann/detail/macro_scope.hpp>
+
+// #include <nlohmann/detail/meta/type_traits.hpp>
+
+
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/// ordered_map: a minimal map-like container that preserves insertion order
+/// for use within nlohmann::basic_json<ordered_map>
+template <class Key, class T, class IgnoredLess = std::less<Key>,
+ class Allocator = std::allocator<std::pair<const Key, T>>>
+ struct ordered_map : std::vector<std::pair<const Key, T>, Allocator>
+{
+ using key_type = Key;
+ using mapped_type = T;
+ using Container = std::vector<std::pair<const Key, T>, Allocator>;
+ using iterator = typename Container::iterator;
+ using const_iterator = typename Container::const_iterator;
+ using size_type = typename Container::size_type;
+ using value_type = typename Container::value_type;
+#ifdef JSON_HAS_CPP_14
+ using key_compare = std::equal_to<>;
+#else
+ using key_compare = std::equal_to<Key>;
+#endif
+
+ // Explicit constructors instead of `using Container::Container`
+ // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4)
+ ordered_map() noexcept(noexcept(Container())) : Container{} {}
+ explicit ordered_map(const Allocator& alloc) noexcept(noexcept(Container(alloc))) : Container{alloc} {}
+ template <class It>
+ ordered_map(It first, It last, const Allocator& alloc = Allocator())
+ : Container{first, last, alloc} {}
+ ordered_map(std::initializer_list<value_type> init, const Allocator& alloc = Allocator() )
+ : Container{init, alloc} {}
+
+ std::pair<iterator, bool> emplace(const key_type& key, T&& t)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return {it, false};
+ }
+ }
+ Container::emplace_back(key, std::forward<T>(t));
+ return {std::prev(this->end()), true};
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ std::pair<iterator, bool> emplace(KeyType && key, T && t)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return {it, false};
+ }
+ }
+ Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));
+ return {std::prev(this->end()), true};
+ }
+
+ T& operator[](const key_type& key)
+ {
+ return emplace(key, T{}).first->second;
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ T & operator[](KeyType && key)
+ {
+ return emplace(std::forward<KeyType>(key), T{}).first->second;
+ }
+
+ const T& operator[](const key_type& key) const
+ {
+ return at(key);
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ const T & operator[](KeyType && key) const
+ {
+ return at(std::forward<KeyType>(key));
+ }
+
+ T& at(const key_type& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ T & at(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ const T& at(const key_type& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ const T & at(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it->second;
+ }
+ }
+
+ JSON_THROW(std::out_of_range("key not found"));
+ }
+
+ size_type erase(const key_type& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ // Since we cannot move const Keys, re-construct them in place
+ for (auto next = it; ++next != this->end(); ++it)
+ {
+ it->~value_type(); // Destroy but keep allocation
+ new (&*it) value_type{std::move(*next)};
+ }
+ Container::pop_back();
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ size_type erase(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ // Since we cannot move const Keys, re-construct them in place
+ for (auto next = it; ++next != this->end(); ++it)
+ {
+ it->~value_type(); // Destroy but keep allocation
+ new (&*it) value_type{std::move(*next)};
+ }
+ Container::pop_back();
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ iterator erase(iterator pos)
+ {
+ return erase(pos, std::next(pos));
+ }
+
+ iterator erase(iterator first, iterator last)
+ {
+ if (first == last)
+ {
+ return first;
+ }
+
+ const auto elements_affected = std::distance(first, last);
+ const auto offset = std::distance(Container::begin(), first);
+
+ // This is the start situation. We need to delete elements_affected
+ // elements (3 in this example: e, f, g), and need to return an
+ // iterator past the last deleted element (h in this example).
+ // Note that offset is the distance from the start of the vector
+ // to first. We will need this later.
+
+ // [ a, b, c, d, e, f, g, h, i, j ]
+ // ^ ^
+ // first last
+
+ // Since we cannot move const Keys, we re-construct them in place.
+ // We start at first and re-construct (viz. copy) the elements from
+ // the back of the vector. Example for first iteration:
+
+ // ,--------.
+ // v | destroy e and re-construct with h
+ // [ a, b, c, d, e, f, g, h, i, j ]
+ // ^ ^
+ // it it + elements_affected
+
+ for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it)
+ {
+ it->~value_type(); // destroy but keep allocation
+ new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it
+ }
+
+ // [ a, b, c, d, h, i, j, h, i, j ]
+ // ^ ^
+ // first last
+
+ // remove the unneeded elements at the end of the vector
+ Container::resize(this->size() - static_cast<size_type>(elements_affected));
+
+ // [ a, b, c, d, h, i, j ]
+ // ^ ^
+ // first last
+
+ // first is now pointing past the last deleted element, but we cannot
+ // use this iterator, because it may have been invalidated by the
+ // resize call. Instead, we can return begin() + offset.
+ return Container::begin() + offset;
+ }
+
+ size_type count(const key_type& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ size_type count(KeyType && key) const // NOLINT(cppcoreguidelines-missing-std-forward)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ iterator find(const key_type& key)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
+ iterator find(KeyType && key) // NOLINT(cppcoreguidelines-missing-std-forward)
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ const_iterator find(const key_type& key) const
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, key))
+ {
+ return it;
+ }
+ }
+ return Container::end();
+ }
+
+ std::pair<iterator, bool> insert( value_type&& value )
+ {
+ return emplace(value.first, std::move(value.second));
+ }
+
+ std::pair<iterator, bool> insert( const value_type& value )
+ {
+ for (auto it = this->begin(); it != this->end(); ++it)
+ {
+ if (m_compare(it->first, value.first))
+ {
+ return {it, false};
+ }
+ }
+ Container::push_back(value);
+ return {--this->end(), true};
+ }
+
+ template<typename InputIt>
+ using require_input_iter = typename std::enable_if<std::is_convertible<typename std::iterator_traits<InputIt>::iterator_category,
+ std::input_iterator_tag>::value>::type;
+
+ template<typename InputIt, typename = require_input_iter<InputIt>>
+ void insert(InputIt first, InputIt last)
+ {
+ for (auto it = first; it != last; ++it)
+ {
+ insert(*it);
+ }
+ }
+
+private:
+ JSON_NO_UNIQUE_ADDRESS key_compare m_compare = key_compare();
+};
+
+NLOHMANN_JSON_NAMESPACE_END
+
+
+#if defined(JSON_HAS_CPP_17)
+ #if JSON_HAS_STATIC_RTTI
+ #include <any>
+ #endif
+ #include <string_view>
+#endif
+
+/*!
+@brief namespace for Niels Lohmann
+@see https://github.com/nlohmann
+@since version 1.0.0
+*/
+NLOHMANN_JSON_NAMESPACE_BEGIN
+
+/*!
+@brief a class to store JSON values
+
+@internal
+@invariant The member variables @a m_value and @a m_type have the following
+relationship:
+- If `m_type == value_t::object`, then `m_value.object != nullptr`.
+- If `m_type == value_t::array`, then `m_value.array != nullptr`.
+- If `m_type == value_t::string`, then `m_value.string != nullptr`.
+The invariants are checked by member function assert_invariant().
+
+@note ObjectType trick from https://stackoverflow.com/a/9860911
+@endinternal
+
+@since version 1.0.0
+
+@nosubgrouping
+*/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
+ : public ::nlohmann::detail::json_base_class<CustomBaseClass>
+{
+ private:
+ template<detail::value_t> friend struct detail::external_constructor;
+
+ template<typename>
+ friend class ::nlohmann::json_pointer;
+ // can be restored when json_pointer backwards compatibility is removed
+ // friend ::nlohmann::json_pointer<StringType>;
+
+ template<typename BasicJsonType, typename InputType>
+ friend class ::nlohmann::detail::parser;
+ friend ::nlohmann::detail::serializer<basic_json>;
+ template<typename BasicJsonType>
+ friend class ::nlohmann::detail::iter_impl;
+ template<typename BasicJsonType, typename CharType>
+ friend class ::nlohmann::detail::binary_writer;
+ template<typename BasicJsonType, typename InputType, typename SAX>
+ friend class ::nlohmann::detail::binary_reader;
+ template<typename BasicJsonType, typename InputAdapterType>
+ friend class ::nlohmann::detail::json_sax_dom_parser;
+ template<typename BasicJsonType, typename InputAdapterType>
+ friend class ::nlohmann::detail::json_sax_dom_callback_parser;
+ friend class ::nlohmann::detail::exception;
+
+ /// workaround type for MSVC
+ using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
+ using json_base_class_t = ::nlohmann::detail::json_base_class<CustomBaseClass>;
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ // convenience aliases for types residing in namespace detail;
+ using lexer = ::nlohmann::detail::lexer_base<basic_json>;
+
+ template<typename InputAdapterType>
+ static ::nlohmann::detail::parser<basic_json, InputAdapterType> parser(
+ InputAdapterType adapter,
+ detail::parser_callback_t<basic_json>cb = nullptr,
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false
+ )
+ {
+ return ::nlohmann::detail::parser<basic_json, InputAdapterType>(std::move(adapter),
+ std::move(cb), allow_exceptions, ignore_comments);
+ }
+
+ private:
+ using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;
+ template<typename BasicJsonType>
+ using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;
+ template<typename BasicJsonType>
+ using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;
+ template<typename Iterator>
+ using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;
+ template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;
+
+ template<typename CharType>
+ using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;
+
+ template<typename InputType>
+ using binary_reader = ::nlohmann::detail::binary_reader<basic_json, InputType>;
+ template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ using serializer = ::nlohmann::detail::serializer<basic_json>;
+
+ public:
+ using value_t = detail::value_t;
+ /// JSON Pointer, see @ref nlohmann::json_pointer
+ using json_pointer = ::nlohmann::json_pointer<StringType>;
+ template<typename T, typename SFINAE>
+ using json_serializer = JSONSerializer<T, SFINAE>;
+ /// how to treat decoding errors
+ using error_handler_t = detail::error_handler_t;
+ /// how to treat CBOR tags
+ using cbor_tag_handler_t = detail::cbor_tag_handler_t;
+ /// how to encode BJData
+ using bjdata_version_t = detail::bjdata_version_t;
+ /// helper type for initializer lists of basic_json values
+ using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;
+
+ using input_format_t = detail::input_format_t;
+ /// SAX interface type, see @ref nlohmann::json_sax
+ using json_sax_t = json_sax<basic_json>;
+
+ ////////////////
+ // exceptions //
+ ////////////////
+
+ /// @name exceptions
+ /// Classes to implement user-defined exceptions.
+ /// @{
+
+ using exception = detail::exception;
+ using parse_error = detail::parse_error;
+ using invalid_iterator = detail::invalid_iterator;
+ using type_error = detail::type_error;
+ using out_of_range = detail::out_of_range;
+ using other_error = detail::other_error;
+
+ /// @}
+
+ /////////////////////
+ // container types //
+ /////////////////////
+
+ /// @name container types
+ /// The canonic container types to use @ref basic_json like any other STL
+ /// container.
+ /// @{
+
+ /// the type of elements in a basic_json container
+ using value_type = basic_json;
+
+ /// the type of an element reference
+ using reference = value_type&;
+ /// the type of an element const reference
+ using const_reference = const value_type&;
+
+ /// a type to represent differences between iterators
+ using difference_type = std::ptrdiff_t;
+ /// a type to represent container sizes
+ using size_type = std::size_t;
+
+ /// the allocator type
+ using allocator_type = AllocatorType<basic_json>;
+
+ /// the type of an element pointer
+ using pointer = typename std::allocator_traits<allocator_type>::pointer;
+ /// the type of an element const pointer
+ using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
+
+ /// an iterator for a basic_json container
+ using iterator = iter_impl<basic_json>;
+ /// a const iterator for a basic_json container
+ using const_iterator = iter_impl<const basic_json>;
+ /// a reverse iterator for a basic_json container
+ using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;
+ /// a const reverse iterator for a basic_json container
+ using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;
+
+ /// @}
+
+ /// @brief returns the allocator associated with the container
+ /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/
+ static allocator_type get_allocator()
+ {
+ return allocator_type();
+ }
+
+ /// @brief returns version information on the library
+ /// @sa https://json.nlohmann.me/api/basic_json/meta/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json meta()
+ {
+ basic_json result;
+
+ result["copyright"] = "(C) 2013-2025 Niels Lohmann";
+ result["name"] = "JSON for Modern C++";
+ result["url"] = "https://github.com/nlohmann/json";
+ result["version"]["string"] =
+ detail::concat(std::to_string(NLOHMANN_JSON_VERSION_MAJOR), '.',
+ std::to_string(NLOHMANN_JSON_VERSION_MINOR), '.',
+ std::to_string(NLOHMANN_JSON_VERSION_PATCH));
+ result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR;
+ result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR;
+ result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH;
+
+#ifdef _WIN32
+ result["platform"] = "win32";
+#elif defined __linux__
+ result["platform"] = "linux";
+#elif defined __APPLE__
+ result["platform"] = "apple";
+#elif defined __unix__
+ result["platform"] = "unix";
+#else
+ result["platform"] = "unknown";
+#endif
+
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}};
+#elif defined(__clang__)
+ result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}};
+#elif defined(__GNUC__) || defined(__GNUG__)
+ result["compiler"] = {{"family", "gcc"}, {"version", detail::concat(
+ std::to_string(__GNUC__), '.',
+ std::to_string(__GNUC_MINOR__), '.',
+ std::to_string(__GNUC_PATCHLEVEL__))
+ }
+ };
+#elif defined(__HP_cc) || defined(__HP_aCC)
+ result["compiler"] = "hp"
+#elif defined(__IBMCPP__)
+ result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}};
+#elif defined(_MSC_VER)
+ result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}};
+#elif defined(__PGI)
+ result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}};
+#elif defined(__SUNPRO_CC)
+ result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}};
+#else
+ result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}};
+#endif
+
+#if defined(_MSVC_LANG)
+ result["compiler"]["c++"] = std::to_string(_MSVC_LANG);
+#elif defined(__cplusplus)
+ result["compiler"]["c++"] = std::to_string(__cplusplus);
+#else
+ result["compiler"]["c++"] = "unknown";
+#endif
+ return result;
+ }
+
+ ///////////////////////////
+ // JSON value data types //
+ ///////////////////////////
+
+ /// @name JSON value data types
+ /// The data types to store a JSON value. These types are derived from
+ /// the template arguments passed to class @ref basic_json.
+ /// @{
+
+ /// @brief default object key comparator type
+ /// The actual object key comparator type (@ref object_comparator_t) may be
+ /// different.
+ /// @sa https://json.nlohmann.me/api/basic_json/default_object_comparator_t/
+#if defined(JSON_HAS_CPP_14)
+ // use of transparent comparator avoids unnecessary repeated construction of temporaries
+ // in functions involving lookup by key with types other than object_t::key_type (aka. StringType)
+ using default_object_comparator_t = std::less<>;
+#else
+ using default_object_comparator_t = std::less<StringType>;
+#endif
+
+ /// @brief a type for an object
+ /// @sa https://json.nlohmann.me/api/basic_json/object_t/
+ using object_t = ObjectType<StringType,
+ basic_json,
+ default_object_comparator_t,
+ AllocatorType<std::pair<const StringType,
+ basic_json>>>;
+
+ /// @brief a type for an array
+ /// @sa https://json.nlohmann.me/api/basic_json/array_t/
+ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
+
+ /// @brief a type for a string
+ /// @sa https://json.nlohmann.me/api/basic_json/string_t/
+ using string_t = StringType;
+
+ /// @brief a type for a boolean
+ /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/
+ using boolean_t = BooleanType;
+
+ /// @brief a type for a number (integer)
+ /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/
+ using number_integer_t = NumberIntegerType;
+
+ /// @brief a type for a number (unsigned)
+ /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/
+ using number_unsigned_t = NumberUnsignedType;
+
+ /// @brief a type for a number (floating-point)
+ /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/
+ using number_float_t = NumberFloatType;
+
+ /// @brief a type for a packed binary type
+ /// @sa https://json.nlohmann.me/api/basic_json/binary_t/
+ using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
+
+ /// @brief object key comparator type
+ /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/
+ using object_comparator_t = detail::actual_object_comparator_t<basic_json>;
+
+ /// @}
+
+ private:
+
+ /// helper for exception-safe object creation
+ template<typename T, typename... Args>
+ JSON_HEDLEY_RETURNS_NON_NULL
+ static T* create(Args&& ... args)
+ {
+ AllocatorType<T> alloc;
+ using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;
+
+ auto deleter = [&](T * obj)
+ {
+ AllocatorTraits::deallocate(alloc, obj, 1);
+ };
+ std::unique_ptr<T, decltype(deleter)> obj(AllocatorTraits::allocate(alloc, 1), deleter);
+ AllocatorTraits::construct(alloc, obj.get(), std::forward<Args>(args)...);
+ JSON_ASSERT(obj != nullptr);
+ return obj.release();
+ }
+
+ ////////////////////////
+ // JSON value storage //
+ ////////////////////////
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ /*!
+ @brief a JSON value
+
+ The actual storage for a JSON value of the @ref basic_json class. This
+ union combines the different storage types for the JSON value types
+ defined in @ref value_t.
+
+ JSON type | value_t type | used type
+ --------- | --------------- | ------------------------
+ object | object | pointer to @ref object_t
+ array | array | pointer to @ref array_t
+ string | string | pointer to @ref string_t
+ boolean | boolean | @ref boolean_t
+ number | number_integer | @ref number_integer_t
+ number | number_unsigned | @ref number_unsigned_t
+ number | number_float | @ref number_float_t
+ binary | binary | pointer to @ref binary_t
+ null | null | *no value is stored*
+
+ @note Variable-length types (objects, arrays, and strings) are stored as
+ pointers. The size of the union should not exceed 64 bits if the default
+ value types are used.
+
+ @since version 1.0.0
+ */
+ union json_value
+ {
+ /// object (stored with pointer to save storage)
+ object_t* object;
+ /// array (stored with pointer to save storage)
+ array_t* array;
+ /// string (stored with pointer to save storage)
+ string_t* string;
+ /// binary (stored with pointer to save storage)
+ binary_t* binary;
+ /// boolean
+ boolean_t boolean;
+ /// number (integer)
+ number_integer_t number_integer;
+ /// number (unsigned integer)
+ number_unsigned_t number_unsigned;
+ /// number (floating-point)
+ number_float_t number_float;
+
+ /// default constructor (for null values)
+ json_value() = default;
+ /// constructor for booleans
+ json_value(boolean_t v) noexcept : boolean(v) {}
+ /// constructor for numbers (integer)
+ json_value(number_integer_t v) noexcept : number_integer(v) {}
+ /// constructor for numbers (unsigned)
+ json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}
+ /// constructor for numbers (floating-point)
+ json_value(number_float_t v) noexcept : number_float(v) {}
+ /// constructor for empty values of a given type
+ json_value(value_t t)
+ {
+ switch (t)
+ {
+ case value_t::object:
+ {
+ object = create<object_t>();
+ break;
+ }
+
+ case value_t::array:
+ {
+ array = create<array_t>();
+ break;
+ }
+
+ case value_t::string:
+ {
+ string = create<string_t>("");
+ break;
+ }
+
+ case value_t::binary:
+ {
+ binary = create<binary_t>();
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ boolean = static_cast<boolean_t>(false);
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ number_integer = static_cast<number_integer_t>(0);
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ number_unsigned = static_cast<number_unsigned_t>(0);
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ number_float = static_cast<number_float_t>(0.0);
+ break;
+ }
+
+ case value_t::null:
+ {
+ object = nullptr; // silence warning, see #821
+ break;
+ }
+
+ case value_t::discarded:
+ default:
+ {
+ object = nullptr; // silence warning, see #821
+ if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
+ {
+ JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.12.0", nullptr)); // LCOV_EXCL_LINE
+ }
+ break;
+ }
+ }
+ }
+
+ /// constructor for strings
+ json_value(const string_t& value) : string(create<string_t>(value)) {}
+
+ /// constructor for rvalue strings
+ json_value(string_t&& value) : string(create<string_t>(std::move(value))) {}
+
+ /// constructor for objects
+ json_value(const object_t& value) : object(create<object_t>(value)) {}
+
+ /// constructor for rvalue objects
+ json_value(object_t&& value) : object(create<object_t>(std::move(value))) {}
+
+ /// constructor for arrays
+ json_value(const array_t& value) : array(create<array_t>(value)) {}
+
+ /// constructor for rvalue arrays
+ json_value(array_t&& value) : array(create<array_t>(std::move(value))) {}
+
+ /// constructor for binary arrays
+ json_value(const typename binary_t::container_type& value) : binary(create<binary_t>(value)) {}
+
+ /// constructor for rvalue binary arrays
+ json_value(typename binary_t::container_type&& value) : binary(create<binary_t>(std::move(value))) {}
+
+ /// constructor for binary arrays (internal type)
+ json_value(const binary_t& value) : binary(create<binary_t>(value)) {}
+
+ /// constructor for rvalue binary arrays (internal type)
+ json_value(binary_t&& value) : binary(create<binary_t>(std::move(value))) {}
+
+ void destroy(value_t t)
+ {
+ if (
+ (t == value_t::object && object == nullptr) ||
+ (t == value_t::array && array == nullptr) ||
+ (t == value_t::string && string == nullptr) ||
+ (t == value_t::binary && binary == nullptr)
+ )
+ {
+ //not initialized (e.g. due to exception in the ctor)
+ return;
+ }
+ if (t == value_t::array || t == value_t::object)
+ {
+ // flatten the current json_value to a heap-allocated stack
+ std::vector<basic_json> stack;
+
+ // move the top-level items to stack
+ if (t == value_t::array)
+ {
+ stack.reserve(array->size());
+ std::move(array->begin(), array->end(), std::back_inserter(stack));
+ }
+ else
+ {
+ stack.reserve(object->size());
+ for (auto&& it : *object)
+ {
+ stack.push_back(std::move(it.second));
+ }
+ }
+
+ while (!stack.empty())
+ {
+ // move the last item to local variable to be processed
+ basic_json current_item(std::move(stack.back()));
+ stack.pop_back();
+
+ // if current_item is array/object, move
+ // its children to the stack to be processed later
+ if (current_item.is_array())
+ {
+ std::move(current_item.m_data.m_value.array->begin(), current_item.m_data.m_value.array->end(), std::back_inserter(stack));
+
+ current_item.m_data.m_value.array->clear();
+ }
+ else if (current_item.is_object())
+ {
+ for (auto&& it : *current_item.m_data.m_value.object)
+ {
+ stack.push_back(std::move(it.second));
+ }
+
+ current_item.m_data.m_value.object->clear();
+ }
+
+ // it's now safe that current_item get destructed
+ // since it doesn't have any children
+ }
+ }
+
+ switch (t)
+ {
+ case value_t::object:
+ {
+ AllocatorType<object_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, object);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);
+ break;
+ }
+
+ case value_t::array:
+ {
+ AllocatorType<array_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, array);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);
+ break;
+ }
+
+ case value_t::string:
+ {
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);
+ break;
+ }
+
+ case value_t::binary:
+ {
+ AllocatorType<binary_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, binary);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, binary, 1);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::discarded:
+ default:
+ {
+ break;
+ }
+ }
+ }
+ };
+
+ private:
+ /*!
+ @brief checks the class invariants
+
+ This function asserts the class invariants. It needs to be called at the
+ end of every constructor to make sure that created objects respect the
+ invariant. Furthermore, it has to be called each time the type of a JSON
+ value is changed, because the invariant expresses a relationship between
+ @a m_type and @a m_value.
+
+ Furthermore, the parent relation is checked for arrays and objects: If
+ @a check_parents true and the value is an array or object, then the
+ container's elements must have the current value as parent.
+
+ @param[in] check_parents whether the parent relation should be checked.
+ The value is true by default and should only be set to false
+ during destruction of objects when the invariant does not
+ need to hold.
+ */
+ void assert_invariant(bool check_parents = true) const noexcept
+ {
+ JSON_ASSERT(m_data.m_type != value_t::object || m_data.m_value.object != nullptr);
+ JSON_ASSERT(m_data.m_type != value_t::array || m_data.m_value.array != nullptr);
+ JSON_ASSERT(m_data.m_type != value_t::string || m_data.m_value.string != nullptr);
+ JSON_ASSERT(m_data.m_type != value_t::binary || m_data.m_value.binary != nullptr);
+
+#if JSON_DIAGNOSTICS
+ JSON_TRY
+ {
+ // cppcheck-suppress assertWithSideEffect
+ JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)
+ {
+ return j.m_parent == this;
+ }));
+ }
+ JSON_CATCH(...) {} // LCOV_EXCL_LINE
+#endif
+ static_cast<void>(check_parents);
+ }
+
+ void set_parents()
+ {
+#if JSON_DIAGNOSTICS
+ switch (m_data.m_type)
+ {
+ case value_t::array:
+ {
+ for (auto& element : *m_data.m_value.array)
+ {
+ element.m_parent = this;
+ }
+ break;
+ }
+
+ case value_t::object:
+ {
+ for (auto& element : *m_data.m_value.object)
+ {
+ element.second.m_parent = this;
+ }
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ break;
+ }
+#endif
+ }
+
+ iterator set_parents(iterator it, typename iterator::difference_type count_set_parents)
+ {
+#if JSON_DIAGNOSTICS
+ for (typename iterator::difference_type i = 0; i < count_set_parents; ++i)
+ {
+ (it + i)->m_parent = this;
+ }
+#else
+ static_cast<void>(count_set_parents);
+#endif
+ return it;
+ }
+
+ reference set_parent(reference j, std::size_t old_capacity = detail::unknown_size())
+ {
+#if JSON_DIAGNOSTICS
+ if (old_capacity != detail::unknown_size())
+ {
+ // see https://github.com/nlohmann/json/issues/2838
+ JSON_ASSERT(type() == value_t::array);
+ if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity))
+ {
+ // capacity has changed: update all parents
+ set_parents();
+ return j;
+ }
+ }
+
+ // ordered_json uses a vector internally, so pointers could have
+ // been invalidated; see https://github.com/nlohmann/json/issues/2962
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning(push )
+#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr
+#endif
+ if (detail::is_ordered_map<object_t>::value)
+ {
+ set_parents();
+ return j;
+ }
+#ifdef JSON_HEDLEY_MSVC_VERSION
+#pragma warning( pop )
+#endif
+
+ j.m_parent = this;
+#else
+ static_cast<void>(j);
+ static_cast<void>(old_capacity);
+#endif
+ return j;
+ }
+
+ public:
+ //////////////////////////
+ // JSON parser callback //
+ //////////////////////////
+
+ /// @brief parser event types
+ /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/
+ using parse_event_t = detail::parse_event_t;
+
+ /// @brief per-element parser callback type
+ /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/
+ using parser_callback_t = detail::parser_callback_t<basic_json>;
+
+ //////////////////
+ // constructors //
+ //////////////////
+
+ /// @name constructors and destructors
+ /// Constructors of class @ref basic_json, copy/move constructor, copy
+ /// assignment, static functions creating objects, and the destructor.
+ /// @{
+
+ /// @brief create an empty value with a given type
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(const value_t v)
+ : m_data(v)
+ {
+ assert_invariant();
+ }
+
+ /// @brief create a null object
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(std::nullptr_t = nullptr) noexcept // NOLINT(bugprone-exception-escape)
+ : basic_json(value_t::null)
+ {
+ assert_invariant();
+ }
+
+ /// @brief create a JSON value from compatible types
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ template < typename CompatibleType,
+ typename U = detail::uncvref_t<CompatibleType>,
+ detail::enable_if_t <
+ !detail::is_basic_json<U>::value && detail::is_compatible_type<basic_json_t, U>::value, int > = 0 >
+ basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape)
+ JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),
+ std::forward<CompatibleType>(val))))
+ {
+ JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief create a JSON value from an existing one
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ template < typename BasicJsonType,
+ detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value&& !std::is_same<basic_json, BasicJsonType>::value, int > = 0 >
+ basic_json(const BasicJsonType& val)
+#if JSON_DIAGNOSTIC_POSITIONS
+ : start_position(val.start_pos()),
+ end_position(val.end_pos())
+#endif
+ {
+ using other_boolean_t = typename BasicJsonType::boolean_t;
+ using other_number_float_t = typename BasicJsonType::number_float_t;
+ using other_number_integer_t = typename BasicJsonType::number_integer_t;
+ using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
+ using other_string_t = typename BasicJsonType::string_t;
+ using other_object_t = typename BasicJsonType::object_t;
+ using other_array_t = typename BasicJsonType::array_t;
+ using other_binary_t = typename BasicJsonType::binary_t;
+
+ switch (val.type())
+ {
+ case value_t::boolean:
+ JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
+ break;
+ case value_t::number_float:
+ JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
+ break;
+ case value_t::number_integer:
+ JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
+ break;
+ case value_t::number_unsigned:
+ JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
+ break;
+ case value_t::string:
+ JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
+ break;
+ case value_t::object:
+ JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
+ break;
+ case value_t::array:
+ JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
+ break;
+ case value_t::binary:
+ JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
+ break;
+ case value_t::null:
+ *this = nullptr;
+ break;
+ case value_t::discarded:
+ m_data.m_type = value_t::discarded;
+ break;
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ JSON_ASSERT(m_data.m_type == val.type());
+
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief create a container (array or object) from an initializer list
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(initializer_list_t init,
+ bool type_deduction = true,
+ value_t manual_type = value_t::array)
+ {
+ // check if each element is an array with two elements whose first
+ // element is a string
+ bool is_an_object = std::all_of(init.begin(), init.end(),
+ [](const detail::json_ref<basic_json>& element_ref)
+ {
+ // The cast is to ensure op[size_type] is called, bearing in mind size_type may not be int;
+ // (many string types can be constructed from 0 via its null-pointer guise, so we get a
+ // broken call to op[key_type], the wrong semantics and a 4804 warning on Windows)
+ return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[static_cast<size_type>(0)].is_string();
+ });
+
+ // adjust type if type deduction is not wanted
+ if (!type_deduction)
+ {
+ // if array is wanted, do not create an object though possible
+ if (manual_type == value_t::array)
+ {
+ is_an_object = false;
+ }
+
+ // if object is wanted but impossible, throw an exception
+ if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
+ {
+ JSON_THROW(type_error::create(301, "cannot create object from initializer list", nullptr));
+ }
+ }
+
+ if (is_an_object)
+ {
+ // the initializer list is a list of pairs -> create object
+ m_data.m_type = value_t::object;
+ m_data.m_value = value_t::object;
+
+ for (auto& element_ref : init)
+ {
+ auto element = element_ref.moved_or_copied();
+ m_data.m_value.object->emplace(
+ std::move(*((*element.m_data.m_value.array)[0].m_data.m_value.string)),
+ std::move((*element.m_data.m_value.array)[1]));
+ }
+ }
+ else
+ {
+ // the initializer list describes an array -> create array
+ m_data.m_type = value_t::array;
+ m_data.m_value.array = create<array_t>(init.begin(), init.end());
+ }
+
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief explicitly create a binary array (without subtype)
+ /// @sa https://json.nlohmann.me/api/basic_json/binary/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json binary(const typename binary_t::container_type& init)
+ {
+ auto res = basic_json();
+ res.m_data.m_type = value_t::binary;
+ res.m_data.m_value = init;
+ return res;
+ }
+
+ /// @brief explicitly create a binary array (with subtype)
+ /// @sa https://json.nlohmann.me/api/basic_json/binary/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype)
+ {
+ auto res = basic_json();
+ res.m_data.m_type = value_t::binary;
+ res.m_data.m_value = binary_t(init, subtype);
+ return res;
+ }
+
+ /// @brief explicitly create a binary array
+ /// @sa https://json.nlohmann.me/api/basic_json/binary/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json binary(typename binary_t::container_type&& init)
+ {
+ auto res = basic_json();
+ res.m_data.m_type = value_t::binary;
+ res.m_data.m_value = std::move(init);
+ return res;
+ }
+
+ /// @brief explicitly create a binary array (with subtype)
+ /// @sa https://json.nlohmann.me/api/basic_json/binary/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype)
+ {
+ auto res = basic_json();
+ res.m_data.m_type = value_t::binary;
+ res.m_data.m_value = binary_t(std::move(init), subtype);
+ return res;
+ }
+
+ /// @brief explicitly create an array from an initializer list
+ /// @sa https://json.nlohmann.me/api/basic_json/array/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json array(initializer_list_t init = {})
+ {
+ return basic_json(init, false, value_t::array);
+ }
+
+ /// @brief explicitly create an object from an initializer list
+ /// @sa https://json.nlohmann.me/api/basic_json/object/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json object(initializer_list_t init = {})
+ {
+ return basic_json(init, false, value_t::object);
+ }
+
+ /// @brief construct an array with count copies of given value
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(size_type cnt, const basic_json& val):
+ m_data{cnt, val}
+ {
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief construct a JSON container given an iterator range
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ template < class InputIT, typename std::enable_if <
+ std::is_same<InputIT, typename basic_json_t::iterator>::value ||
+ std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int >::type = 0 >
+ basic_json(InputIT first, InputIT last) // NOLINT(performance-unnecessary-value-param)
+ {
+ JSON_ASSERT(first.m_object != nullptr);
+ JSON_ASSERT(last.m_object != nullptr);
+
+ // make sure iterator fits the current value
+ if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", nullptr));
+ }
+
+ // copy type from first iterator
+ m_data.m_type = first.m_object->m_data.m_type;
+
+ // check if iterator range is complete for primitive values
+ switch (m_data.m_type)
+ {
+ case value_t::boolean:
+ case value_t::number_float:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::string:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
+ || !last.m_it.primitive_iterator.is_end()))
+ {
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range", first.m_object));
+ }
+ break;
+ }
+
+ case value_t::null:
+ case value_t::object:
+ case value_t::array:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ break;
+ }
+
+ switch (m_data.m_type)
+ {
+ case value_t::number_integer:
+ {
+ m_data.m_value.number_integer = first.m_object->m_data.m_value.number_integer;
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ m_data.m_value.number_unsigned = first.m_object->m_data.m_value.number_unsigned;
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ m_data.m_value.number_float = first.m_object->m_data.m_value.number_float;
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ m_data.m_value.boolean = first.m_object->m_data.m_value.boolean;
+ break;
+ }
+
+ case value_t::string:
+ {
+ m_data.m_value = *first.m_object->m_data.m_value.string;
+ break;
+ }
+
+ case value_t::object:
+ {
+ m_data.m_value.object = create<object_t>(first.m_it.object_iterator,
+ last.m_it.object_iterator);
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_data.m_value.array = create<array_t>(first.m_it.array_iterator,
+ last.m_it.array_iterator);
+ break;
+ }
+
+ case value_t::binary:
+ {
+ m_data.m_value = *first.m_object->m_data.m_value.binary;
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ JSON_THROW(invalid_iterator::create(206, detail::concat("cannot construct with iterators from ", first.m_object->type_name()), first.m_object));
+ }
+
+ set_parents();
+ assert_invariant();
+ }
+
+ ///////////////////////////////////////
+ // other constructors and destructor //
+ ///////////////////////////////////////
+
+ template<typename JsonRef,
+ detail::enable_if_t<detail::conjunction<detail::is_json_ref<JsonRef>,
+ std::is_same<typename JsonRef::value_type, basic_json>>::value, int> = 0 >
+ basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {}
+
+ /// @brief copy constructor
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(const basic_json& other)
+ : json_base_class_t(other)
+#if JSON_DIAGNOSTIC_POSITIONS
+ , start_position(other.start_position)
+ , end_position(other.end_position)
+#endif
+ {
+ m_data.m_type = other.m_data.m_type;
+ // check of passed value is valid
+ other.assert_invariant();
+
+ switch (m_data.m_type)
+ {
+ case value_t::object:
+ {
+ m_data.m_value = *other.m_data.m_value.object;
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_data.m_value = *other.m_data.m_value.array;
+ break;
+ }
+
+ case value_t::string:
+ {
+ m_data.m_value = *other.m_data.m_value.string;
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ m_data.m_value = other.m_data.m_value.boolean;
+ break;
+ }
+
+ case value_t::number_integer:
+ {
+ m_data.m_value = other.m_data.m_value.number_integer;
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ m_data.m_value = other.m_data.m_value.number_unsigned;
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ m_data.m_value = other.m_data.m_value.number_float;
+ break;
+ }
+
+ case value_t::binary:
+ {
+ m_data.m_value = *other.m_data.m_value.binary;
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ break;
+ }
+
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief move constructor
+ /// @sa https://json.nlohmann.me/api/basic_json/basic_json/
+ basic_json(basic_json&& other) noexcept
+ : json_base_class_t(std::forward<json_base_class_t>(other)),
+ m_data(std::move(other.m_data)) // cppcheck-suppress[accessForwarded] TODO check
+#if JSON_DIAGNOSTIC_POSITIONS
+ , start_position(other.start_position) // cppcheck-suppress[accessForwarded] TODO check
+ , end_position(other.end_position) // cppcheck-suppress[accessForwarded] TODO check
+#endif
+ {
+ // check that passed value is valid
+ other.assert_invariant(false); // cppcheck-suppress[accessForwarded]
+
+ // invalidate payload
+ other.m_data.m_type = value_t::null;
+ other.m_data.m_value = {};
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ other.start_position = std::string::npos;
+ other.end_position = std::string::npos;
+#endif
+
+ set_parents();
+ assert_invariant();
+ }
+
+ /// @brief copy assignment
+ /// @sa https://json.nlohmann.me/api/basic_json/operator=/
+ basic_json& operator=(basic_json other) noexcept (
+ std::is_nothrow_move_constructible<value_t>::value&&
+ std::is_nothrow_move_assignable<value_t>::value&&
+ std::is_nothrow_move_constructible<json_value>::value&&
+ std::is_nothrow_move_assignable<json_value>::value&&
+ std::is_nothrow_move_assignable<json_base_class_t>::value
+ )
+ {
+ // check that passed value is valid
+ other.assert_invariant();
+
+ using std::swap;
+ swap(m_data.m_type, other.m_data.m_type);
+ swap(m_data.m_value, other.m_data.m_value);
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ swap(start_position, other.start_position);
+ swap(end_position, other.end_position);
+#endif
+
+ json_base_class_t::operator=(std::move(other));
+
+ set_parents();
+ assert_invariant();
+ return *this;
+ }
+
+ /// @brief destructor
+ /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/
+ ~basic_json() noexcept
+ {
+ assert_invariant(false);
+ }
+
+ /// @}
+
+ public:
+ ///////////////////////
+ // object inspection //
+ ///////////////////////
+
+ /// @name object inspection
+ /// Functions to inspect the type of a JSON value.
+ /// @{
+
+ /// @brief serialization
+ /// @sa https://json.nlohmann.me/api/basic_json/dump/
+ string_t dump(const int indent = -1,
+ const char indent_char = ' ',
+ const bool ensure_ascii = false,
+ const error_handler_t error_handler = error_handler_t::strict) const
+ {
+ string_t result;
+ serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);
+
+ if (indent >= 0)
+ {
+ s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));
+ }
+ else
+ {
+ s.dump(*this, false, ensure_ascii, 0);
+ }
+
+ return result;
+ }
+
+ /// @brief return the type of the JSON value (explicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/type/
+ constexpr value_t type() const noexcept
+ {
+ return m_data.m_type;
+ }
+
+ /// @brief return whether type is primitive
+ /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/
+ constexpr bool is_primitive() const noexcept
+ {
+ return is_null() || is_string() || is_boolean() || is_number() || is_binary();
+ }
+
+ /// @brief return whether type is structured
+ /// @sa https://json.nlohmann.me/api/basic_json/is_structured/
+ constexpr bool is_structured() const noexcept
+ {
+ return is_array() || is_object();
+ }
+
+ /// @brief return whether value is null
+ /// @sa https://json.nlohmann.me/api/basic_json/is_null/
+ constexpr bool is_null() const noexcept
+ {
+ return m_data.m_type == value_t::null;
+ }
+
+ /// @brief return whether value is a boolean
+ /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/
+ constexpr bool is_boolean() const noexcept
+ {
+ return m_data.m_type == value_t::boolean;
+ }
+
+ /// @brief return whether value is a number
+ /// @sa https://json.nlohmann.me/api/basic_json/is_number/
+ constexpr bool is_number() const noexcept
+ {
+ return is_number_integer() || is_number_float();
+ }
+
+ /// @brief return whether value is an integer number
+ /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/
+ constexpr bool is_number_integer() const noexcept
+ {
+ return m_data.m_type == value_t::number_integer || m_data.m_type == value_t::number_unsigned;
+ }
+
+ /// @brief return whether value is an unsigned integer number
+ /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/
+ constexpr bool is_number_unsigned() const noexcept
+ {
+ return m_data.m_type == value_t::number_unsigned;
+ }
+
+ /// @brief return whether value is a floating-point number
+ /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/
+ constexpr bool is_number_float() const noexcept
+ {
+ return m_data.m_type == value_t::number_float;
+ }
+
+ /// @brief return whether value is an object
+ /// @sa https://json.nlohmann.me/api/basic_json/is_object/
+ constexpr bool is_object() const noexcept
+ {
+ return m_data.m_type == value_t::object;
+ }
+
+ /// @brief return whether value is an array
+ /// @sa https://json.nlohmann.me/api/basic_json/is_array/
+ constexpr bool is_array() const noexcept
+ {
+ return m_data.m_type == value_t::array;
+ }
+
+ /// @brief return whether value is a string
+ /// @sa https://json.nlohmann.me/api/basic_json/is_string/
+ constexpr bool is_string() const noexcept
+ {
+ return m_data.m_type == value_t::string;
+ }
+
+ /// @brief return whether value is a binary array
+ /// @sa https://json.nlohmann.me/api/basic_json/is_binary/
+ constexpr bool is_binary() const noexcept
+ {
+ return m_data.m_type == value_t::binary;
+ }
+
+ /// @brief return whether value is discarded
+ /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/
+ constexpr bool is_discarded() const noexcept
+ {
+ return m_data.m_type == value_t::discarded;
+ }
+
+ /// @brief return the type of the JSON value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/
+ constexpr operator value_t() const noexcept
+ {
+ return m_data.m_type;
+ }
+
+ /// @}
+
+ private:
+ //////////////////
+ // value access //
+ //////////////////
+
+ /// get a boolean (explicit)
+ boolean_t get_impl(boolean_t* /*unused*/) const
+ {
+ if (JSON_HEDLEY_LIKELY(is_boolean()))
+ {
+ return m_data.m_value.boolean;
+ }
+
+ JSON_THROW(type_error::create(302, detail::concat("type must be boolean, but is ", type_name()), this));
+ }
+
+ /// get a pointer to the value (object)
+ object_t* get_impl_ptr(object_t* /*unused*/) noexcept
+ {
+ return is_object() ? m_data.m_value.object : nullptr;
+ }
+
+ /// get a pointer to the value (object)
+ constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept
+ {
+ return is_object() ? m_data.m_value.object : nullptr;
+ }
+
+ /// get a pointer to the value (array)
+ array_t* get_impl_ptr(array_t* /*unused*/) noexcept
+ {
+ return is_array() ? m_data.m_value.array : nullptr;
+ }
+
+ /// get a pointer to the value (array)
+ constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept
+ {
+ return is_array() ? m_data.m_value.array : nullptr;
+ }
+
+ /// get a pointer to the value (string)
+ string_t* get_impl_ptr(string_t* /*unused*/) noexcept
+ {
+ return is_string() ? m_data.m_value.string : nullptr;
+ }
+
+ /// get a pointer to the value (string)
+ constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept
+ {
+ return is_string() ? m_data.m_value.string : nullptr;
+ }
+
+ /// get a pointer to the value (boolean)
+ boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept
+ {
+ return is_boolean() ? &m_data.m_value.boolean : nullptr;
+ }
+
+ /// get a pointer to the value (boolean)
+ constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept
+ {
+ return is_boolean() ? &m_data.m_value.boolean : nullptr;
+ }
+
+ /// get a pointer to the value (integer number)
+ number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept
+ {
+ return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr;
+ }
+
+ /// get a pointer to the value (integer number)
+ constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept
+ {
+ return m_data.m_type == value_t::number_integer ? &m_data.m_value.number_integer : nullptr;
+ }
+
+ /// get a pointer to the value (unsigned number)
+ number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept
+ {
+ return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;
+ }
+
+ /// get a pointer to the value (unsigned number)
+ constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept
+ {
+ return is_number_unsigned() ? &m_data.m_value.number_unsigned : nullptr;
+ }
+
+ /// get a pointer to the value (floating-point number)
+ number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept
+ {
+ return is_number_float() ? &m_data.m_value.number_float : nullptr;
+ }
+
+ /// get a pointer to the value (floating-point number)
+ constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept
+ {
+ return is_number_float() ? &m_data.m_value.number_float : nullptr;
+ }
+
+ /// get a pointer to the value (binary)
+ binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept
+ {
+ return is_binary() ? m_data.m_value.binary : nullptr;
+ }
+
+ /// get a pointer to the value (binary)
+ constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept
+ {
+ return is_binary() ? m_data.m_value.binary : nullptr;
+ }
+
+ /*!
+ @brief helper function to implement get_ref()
+
+ This function helps to implement get_ref() without code duplication for
+ const and non-const overloads
+
+ @tparam ThisType will be deduced as `basic_json` or `const basic_json`
+
+ @throw type_error.303 if ReferenceType does not match underlying value
+ type of the current JSON
+ */
+ template<typename ReferenceType, typename ThisType>
+ static ReferenceType get_ref_impl(ThisType& obj)
+ {
+ // delegate the call to get_ptr<>()
+ auto* ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();
+
+ if (JSON_HEDLEY_LIKELY(ptr != nullptr))
+ {
+ return *ptr;
+ }
+
+ JSON_THROW(type_error::create(303, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
+ }
+
+ public:
+ /// @name value access
+ /// Direct access to the stored value of a JSON value.
+ /// @{
+
+ /// @brief get a pointer value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
+ auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+ {
+ // delegate the call to get_impl_ptr<>()
+ return get_impl_ptr(static_cast<PointerType>(nullptr));
+ }
+
+ /// @brief get a pointer value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/
+ template < typename PointerType, typename std::enable_if <
+ std::is_pointer<PointerType>::value&&
+ std::is_const<typename std::remove_pointer<PointerType>::type>::value, int >::type = 0 >
+ constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))
+ {
+ // delegate the call to get_impl_ptr<>() const
+ return get_impl_ptr(static_cast<PointerType>(nullptr));
+ }
+
+ private:
+ /*!
+ @brief get a value (explicit)
+
+ Explicit type conversion between the JSON value and a compatible value
+ which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+ and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
+ The value is converted by calling the @ref json_serializer<ValueType>
+ `from_json()` method.
+
+ The function is equivalent to executing
+ @code {.cpp}
+ ValueType ret;
+ JSONSerializer<ValueType>::from_json(*this, ret);
+ return ret;
+ @endcode
+
+ This overloads is chosen if:
+ - @a ValueType is not @ref basic_json,
+ - @ref json_serializer<ValueType> has a `from_json()` method of the form
+ `void from_json(const basic_json&, ValueType&)`, and
+ - @ref json_serializer<ValueType> does not have a `from_json()` method of
+ the form `ValueType from_json(const basic_json&)`
+
+ @tparam ValueType the returned value type
+
+ @return copy of the JSON value, converted to @a ValueType
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+ @liveexample{The example below shows several conversions from JSON values
+ to other types. There a few things to note: (1) Floating-point numbers can
+ be converted to integers\, (2) A JSON array can be converted to a standard
+ `std::vector<short>`\, (3) A JSON object can be converted to C++
+ associative containers such as `std::unordered_map<std::string\,
+ json>`.,get__ValueType_const}
+
+ @since version 2.1.0
+ */
+ template < typename ValueType,
+ detail::enable_if_t <
+ detail::is_default_constructible<ValueType>::value&&
+ detail::has_from_json<basic_json_t, ValueType>::value,
+ int > = 0 >
+ ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))
+ {
+ auto ret = ValueType();
+ JSONSerializer<ValueType>::from_json(*this, ret);
+ return ret;
+ }
+
+ /*!
+ @brief get a value (explicit); special case
+
+ Explicit type conversion between the JSON value and a compatible value
+ which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)
+ and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).
+ The value is converted by calling the @ref json_serializer<ValueType>
+ `from_json()` method.
+
+ The function is equivalent to executing
+ @code {.cpp}
+ return JSONSerializer<ValueType>::from_json(*this);
+ @endcode
+
+ This overloads is chosen if:
+ - @a ValueType is not @ref basic_json and
+ - @ref json_serializer<ValueType> has a `from_json()` method of the form
+ `ValueType from_json(const basic_json&)`
+
+ @note If @ref json_serializer<ValueType> has both overloads of
+ `from_json()`, this one is chosen.
+
+ @tparam ValueType the returned value type
+
+ @return copy of the JSON value, converted to @a ValueType
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws
+
+ @since version 2.1.0
+ */
+ template < typename ValueType,
+ detail::enable_if_t <
+ detail::has_non_default_from_json<basic_json_t, ValueType>::value,
+ int > = 0 >
+ ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>())))
+ {
+ return JSONSerializer<ValueType>::from_json(*this);
+ }
+
+ /*!
+ @brief get special-case overload
+
+ This overloads converts the current @ref basic_json in a different
+ @ref basic_json type
+
+ @tparam BasicJsonType == @ref basic_json
+
+ @return a copy of *this, converted into @a BasicJsonType
+
+ @complexity Depending on the implementation of the called `from_json()`
+ method.
+
+ @since version 3.2.0
+ */
+ template < typename BasicJsonType,
+ detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value,
+ int > = 0 >
+ BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const
+ {
+ return *this;
+ }
+
+ /*!
+ @brief get special-case overload
+
+ This overloads avoids a lot of template boilerplate, it can be seen as the
+ identity method
+
+ @tparam BasicJsonType == @ref basic_json
+
+ @return a copy of *this
+
+ @complexity Constant.
+
+ @since version 2.1.0
+ */
+ template<typename BasicJsonType,
+ detail::enable_if_t<
+ std::is_same<BasicJsonType, basic_json_t>::value,
+ int> = 0>
+ basic_json get_impl(detail::priority_tag<3> /*unused*/) const
+ {
+ return *this;
+ }
+
+ /*!
+ @brief get a pointer value (explicit)
+ @copydoc get()
+ */
+ template<typename PointerType,
+ detail::enable_if_t<
+ std::is_pointer<PointerType>::value,
+ int> = 0>
+ constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept
+ -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())
+ {
+ // delegate the call to get_ptr
+ return get_ptr<PointerType>();
+ }
+
+ public:
+ /*!
+ @brief get a (pointer) value (explicit)
+
+ Performs explicit type conversion between the JSON value and a compatible value if required.
+
+ - If the requested type is a pointer to the internally stored JSON value that pointer is returned.
+ No copies are made.
+
+ - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible
+ from the current @ref basic_json.
+
+ - Otherwise the value is converted by calling the @ref json_serializer<ValueType> `from_json()`
+ method.
+
+ @tparam ValueTypeCV the provided value type
+ @tparam ValueType the returned value type
+
+ @return copy of the JSON value, converted to @tparam ValueType if necessary
+
+ @throw what @ref json_serializer<ValueType> `from_json()` method throws if conversion is required
+
+ @since version 2.1.0
+ */
+ template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>>
+#if defined(JSON_HAS_CPP_14)
+ constexpr
+#endif
+ auto get() const noexcept(
+ noexcept(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {})))
+ -> decltype(std::declval<const basic_json_t&>().template get_impl<ValueType>(detail::priority_tag<4> {}))
+ {
+ // we cannot static_assert on ValueTypeCV being non-const, because
+ // there is support for get<const basic_json_t>(), which is why we
+ // still need the uncvref
+ static_assert(!std::is_reference<ValueTypeCV>::value,
+ "get() cannot be used with reference types, you might want to use get_ref()");
+ return get_impl<ValueType>(detail::priority_tag<4> {});
+ }
+
+ /*!
+ @brief get a pointer value (explicit)
+
+ Explicit pointer access to the internally stored JSON value. No copies are
+ made.
+
+ @warning The pointer becomes invalid if the underlying JSON object
+ changes.
+
+ @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref
+ object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,
+ @ref number_unsigned_t, or @ref number_float_t.
+
+ @return pointer to the internally stored JSON value if the requested
+ pointer type @a PointerType fits to the JSON value; `nullptr` otherwise
+
+ @complexity Constant.
+
+ @liveexample{The example below shows how pointers to internal values of a
+ JSON value can be requested. Note that no type conversions are made and a
+ `nullptr` is returned if the value and the requested pointer type does not
+ match.,get__PointerType}
+
+ @sa see @ref get_ptr() for explicit pointer-member access
+
+ @since version 1.0.0
+ */
+ template<typename PointerType, typename std::enable_if<
+ std::is_pointer<PointerType>::value, int>::type = 0>
+ auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())
+ {
+ // delegate the call to get_ptr
+ return get_ptr<PointerType>();
+ }
+
+ /// @brief get a value (explicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_to/
+ template < typename ValueType,
+ detail::enable_if_t <
+ !detail::is_basic_json<ValueType>::value&&
+ detail::has_from_json<basic_json_t, ValueType>::value,
+ int > = 0 >
+ ValueType & get_to(ValueType& v) const noexcept(noexcept(
+ JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))
+ {
+ JSONSerializer<ValueType>::from_json(*this, v);
+ return v;
+ }
+
+ // specialization to allow calling get_to with a basic_json value
+ // see https://github.com/nlohmann/json/issues/2175
+ template<typename ValueType,
+ detail::enable_if_t <
+ detail::is_basic_json<ValueType>::value,
+ int> = 0>
+ ValueType & get_to(ValueType& v) const
+ {
+ v = *this;
+ return v;
+ }
+
+ template <
+ typename T, std::size_t N,
+ typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ detail::enable_if_t <
+ detail::has_from_json<basic_json_t, Array>::value, int > = 0 >
+ Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
+ noexcept(noexcept(JSONSerializer<Array>::from_json(
+ std::declval<const basic_json_t&>(), v)))
+ {
+ JSONSerializer<Array>::from_json(*this, v);
+ return v;
+ }
+
+ /// @brief get a reference value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
+ template<typename ReferenceType, typename std::enable_if<
+ std::is_reference<ReferenceType>::value, int>::type = 0>
+ ReferenceType get_ref()
+ {
+ // delegate call to get_ref_impl
+ return get_ref_impl<ReferenceType>(*this);
+ }
+
+ /// @brief get a reference value (implicit)
+ /// @sa https://json.nlohmann.me/api/basic_json/get_ref/
+ template < typename ReferenceType, typename std::enable_if <
+ std::is_reference<ReferenceType>::value&&
+ std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int >::type = 0 >
+ ReferenceType get_ref() const
+ {
+ // delegate call to get_ref_impl
+ return get_ref_impl<ReferenceType>(*this);
+ }
+
+ /*!
+ @brief get a value (implicit)
+
+ Implicit type conversion between the JSON value and a compatible value.
+ The call is realized by calling @ref get() const.
+
+ @tparam ValueType non-pointer type compatible to the JSON value, for
+ instance `int` for JSON integer numbers, `bool` for JSON booleans, or
+ `std::vector` types for JSON arrays. The character type of @ref string_t
+ as well as an initializer list of this type is excluded to avoid
+ ambiguities as these types implicitly convert to `std::string`.
+
+ @return copy of the JSON value, converted to type @a ValueType
+
+ @throw type_error.302 in case passed type @a ValueType is incompatible
+ to the JSON value type (e.g., the JSON value is of type boolean, but a
+ string is requested); see example below
+
+ @complexity Linear in the size of the JSON value.
+
+ @liveexample{The example below shows several conversions from JSON values
+ to other types. There a few things to note: (1) Floating-point numbers can
+ be converted to integers\, (2) A JSON array can be converted to a standard
+ `std::vector<short>`\, (3) A JSON object can be converted to C++
+ associative containers such as `std::unordered_map<std::string\,
+ json>`.,operator__ValueType}
+
+ @since version 1.0.0
+ */
+ template < typename ValueType, typename std::enable_if <
+ detail::conjunction <
+ detail::negation<std::is_pointer<ValueType>>,
+ detail::negation<std::is_same<ValueType, std::nullptr_t>>,
+ detail::negation<std::is_same<ValueType, detail::json_ref<basic_json>>>,
+ detail::negation<std::is_same<ValueType, typename string_t::value_type>>,
+ detail::negation<detail::is_basic_json<ValueType>>,
+ detail::negation<std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>>,
+#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914))
+ detail::negation<std::is_same<ValueType, std::string_view>>,
+#endif
+#if defined(JSON_HAS_CPP_17) && JSON_HAS_STATIC_RTTI
+ detail::negation<std::is_same<ValueType, std::any>>,
+#endif
+ detail::is_detected_lazy<detail::get_template_function, const basic_json_t&, ValueType>
+ >::value, int >::type = 0 >
+ JSON_EXPLICIT operator ValueType() const
+ {
+ // delegate the call to get<>() const
+ return get<ValueType>();
+ }
+
+ /// @brief get a binary value
+ /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+ binary_t& get_binary()
+ {
+ if (!is_binary())
+ {
+ JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+ }
+
+ return *get_ptr<binary_t*>();
+ }
+
+ /// @brief get a binary value
+ /// @sa https://json.nlohmann.me/api/basic_json/get_binary/
+ const binary_t& get_binary() const
+ {
+ if (!is_binary())
+ {
+ JSON_THROW(type_error::create(302, detail::concat("type must be binary, but is ", type_name()), this));
+ }
+
+ return *get_ptr<const binary_t*>();
+ }
+
+ /// @}
+
+ ////////////////////
+ // element access //
+ ////////////////////
+
+ /// @name element access
+ /// Access to the JSON value.
+ /// @{
+
+ /// @brief access specified array element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ reference at(size_type idx)
+ {
+ // at only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ JSON_TRY
+ {
+ return set_parent(m_data.m_value.array->at(idx));
+ }
+ JSON_CATCH (std::out_of_range&)
+ {
+ // create better exception explanation
+ JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+ } // cppcheck-suppress[missingReturn]
+ }
+ else
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+ }
+
+ /// @brief access specified array element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ const_reference at(size_type idx) const
+ {
+ // at only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ JSON_TRY
+ {
+ return m_data.m_value.array->at(idx);
+ }
+ JSON_CATCH (std::out_of_range&)
+ {
+ // create better exception explanation
+ JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+ } // cppcheck-suppress[missingReturn]
+ }
+ else
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+ }
+
+ /// @brief access specified object element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ reference at(const typename object_t::key_type& key)
+ {
+ // at only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+
+ auto it = m_data.m_value.object->find(key);
+ if (it == m_data.m_value.object->end())
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+ }
+ return set_parent(it->second);
+ }
+
+ /// @brief access specified object element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ reference at(KeyType && key)
+ {
+ // at only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+
+ auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
+ if (it == m_data.m_value.object->end())
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+ }
+ return set_parent(it->second);
+ }
+
+ /// @brief access specified object element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ const_reference at(const typename object_t::key_type& key) const
+ {
+ // at only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+
+ auto it = m_data.m_value.object->find(key);
+ if (it == m_data.m_value.object->end())
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", key, "' not found"), this));
+ }
+ return it->second;
+ }
+
+ /// @brief access specified object element with bounds checking
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ const_reference at(KeyType && key) const
+ {
+ // at only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(304, detail::concat("cannot use at() with ", type_name()), this));
+ }
+
+ auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
+ if (it == m_data.m_value.object->end())
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", string_t(std::forward<KeyType>(key)), "' not found"), this));
+ }
+ return it->second;
+ }
+
+ /// @brief access specified array element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ reference operator[](size_type idx)
+ {
+ // implicitly convert null value to an empty array
+ if (is_null())
+ {
+ m_data.m_type = value_t::array;
+ m_data.m_value.array = create<array_t>();
+ assert_invariant();
+ }
+
+ // operator[] only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ // fill up array with null values if given idx is outside range
+ if (idx >= m_data.m_value.array->size())
+ {
+#if JSON_DIAGNOSTICS
+ // remember array size & capacity before resizing
+ const auto old_size = m_data.m_value.array->size();
+ const auto old_capacity = m_data.m_value.array->capacity();
+#endif
+ m_data.m_value.array->resize(idx + 1);
+
+#if JSON_DIAGNOSTICS
+ if (JSON_HEDLEY_UNLIKELY(m_data.m_value.array->capacity() != old_capacity))
+ {
+ // capacity has changed: update all parents
+ set_parents();
+ }
+ else
+ {
+ // set parent for values added above
+ set_parents(begin() + static_cast<typename iterator::difference_type>(old_size), static_cast<typename iterator::difference_type>(idx + 1 - old_size));
+ }
+#endif
+ assert_invariant();
+ }
+
+ return m_data.m_value.array->operator[](idx);
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
+ }
+
+ /// @brief access specified array element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ const_reference operator[](size_type idx) const
+ {
+ // const operator[] only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ return m_data.m_value.array->operator[](idx);
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a numeric argument with ", type_name()), this));
+ }
+
+ /// @brief access specified object element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ reference operator[](typename object_t::key_type key) // NOLINT(performance-unnecessary-value-param)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_data.m_type = value_t::object;
+ m_data.m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ // operator[] only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ auto result = m_data.m_value.object->emplace(std::move(key), nullptr);
+ return set_parent(result.first->second);
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+ }
+
+ /// @brief access specified object element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ const_reference operator[](const typename object_t::key_type& key) const
+ {
+ // const operator[] only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ auto it = m_data.m_value.object->find(key);
+ JSON_ASSERT(it != m_data.m_value.object->end());
+ return it->second;
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+ }
+
+ // these two functions resolve a (const) char * ambiguity affecting Clang and MSVC
+ // (they seemingly cannot be constrained to resolve the ambiguity)
+ template<typename T>
+ reference operator[](T* key)
+ {
+ return operator[](typename object_t::key_type(key));
+ }
+
+ template<typename T>
+ const_reference operator[](T* key) const
+ {
+ return operator[](typename object_t::key_type(key));
+ }
+
+ /// @brief access specified object element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+ reference operator[](KeyType && key)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_data.m_type = value_t::object;
+ m_data.m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ // operator[] only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ auto result = m_data.m_value.object->emplace(std::forward<KeyType>(key), nullptr);
+ return set_parent(result.first->second);
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+ }
+
+ /// @brief access specified object element
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int > = 0 >
+ const_reference operator[](KeyType && key) const
+ {
+ // const operator[] only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
+ JSON_ASSERT(it != m_data.m_value.object->end());
+ return it->second;
+ }
+
+ JSON_THROW(type_error::create(305, detail::concat("cannot use operator[] with a string argument with ", type_name()), this));
+ }
+
+ private:
+ template<typename KeyType>
+ using is_comparable_with_object_key = detail::is_comparable <
+ object_comparator_t, const typename object_t::key_type&, KeyType >;
+
+ template<typename ValueType>
+ using value_return_type = std::conditional <
+ detail::is_c_string_uncvref<ValueType>::value,
+ string_t, typename std::decay<ValueType>::type >;
+
+ public:
+ /// @brief access specified object element with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, detail::enable_if_t <
+ !detail::is_transparent<object_comparator_t>::value
+ && detail::is_getable<basic_json_t, ValueType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if key is found, return value and given default value otherwise
+ const auto it = find(key);
+ if (it != end())
+ {
+ return it->template get<ValueType>();
+ }
+
+ return default_value;
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+ detail::enable_if_t <
+ !detail::is_transparent<object_comparator_t>::value
+ && detail::is_getable<basic_json_t, ReturnType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ReturnType value(const typename object_t::key_type& key, ValueType && default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if key is found, return value and given default value otherwise
+ const auto it = find(key);
+ if (it != end())
+ {
+ return it->template get<ReturnType>();
+ }
+
+ return std::forward<ValueType>(default_value);
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, class KeyType, detail::enable_if_t <
+ detail::is_transparent<object_comparator_t>::value
+ && !detail::is_json_pointer<KeyType>::value
+ && is_comparable_with_object_key<KeyType>::value
+ && detail::is_getable<basic_json_t, ValueType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ValueType value(KeyType && key, const ValueType& default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if key is found, return value and given default value otherwise
+ const auto it = find(std::forward<KeyType>(key));
+ if (it != end())
+ {
+ return it->template get<ValueType>();
+ }
+
+ return default_value;
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element via JSON Pointer with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, class KeyType, class ReturnType = typename value_return_type<ValueType>::type,
+ detail::enable_if_t <
+ detail::is_transparent<object_comparator_t>::value
+ && !detail::is_json_pointer<KeyType>::value
+ && is_comparable_with_object_key<KeyType>::value
+ && detail::is_getable<basic_json_t, ReturnType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ReturnType value(KeyType && key, ValueType && default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if key is found, return value and given default value otherwise
+ const auto it = find(std::forward<KeyType>(key));
+ if (it != end())
+ {
+ return it->template get<ReturnType>();
+ }
+
+ return std::forward<ValueType>(default_value);
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element via JSON Pointer with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, detail::enable_if_t <
+ detail::is_getable<basic_json_t, ValueType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ValueType value(const json_pointer& ptr, const ValueType& default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if pointer resolves a value, return it or use default value
+ JSON_TRY
+ {
+ return ptr.get_checked(this).template get<ValueType>();
+ }
+ JSON_INTERNAL_CATCH (out_of_range&)
+ {
+ return default_value;
+ }
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ /// @brief access specified object element via JSON Pointer with default value
+ /// @sa https://json.nlohmann.me/api/basic_json/value/
+ template < class ValueType, class ReturnType = typename value_return_type<ValueType>::type,
+ detail::enable_if_t <
+ detail::is_getable<basic_json_t, ReturnType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ ReturnType value(const json_pointer& ptr, ValueType && default_value) const
+ {
+ // value only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ // if pointer resolves a value, return it or use default value
+ JSON_TRY
+ {
+ return ptr.get_checked(this).template get<ReturnType>();
+ }
+ JSON_INTERNAL_CATCH (out_of_range&)
+ {
+ return std::forward<ValueType>(default_value);
+ }
+ }
+
+ JSON_THROW(type_error::create(306, detail::concat("cannot use value() with ", type_name()), this));
+ }
+
+ template < class ValueType, class BasicJsonType, detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value
+ && detail::is_getable<basic_json_t, ValueType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ ValueType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, const ValueType& default_value) const
+ {
+ return value(ptr.convert(), default_value);
+ }
+
+ template < class ValueType, class BasicJsonType, class ReturnType = typename value_return_type<ValueType>::type,
+ detail::enable_if_t <
+ detail::is_basic_json<BasicJsonType>::value
+ && detail::is_getable<basic_json_t, ReturnType>::value
+ && !std::is_same<value_t, detail::uncvref_t<ValueType>>::value, int > = 0 >
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ ReturnType value(const ::nlohmann::json_pointer<BasicJsonType>& ptr, ValueType && default_value) const
+ {
+ return value(ptr.convert(), std::forward<ValueType>(default_value));
+ }
+
+ /// @brief access the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/front/
+ reference front()
+ {
+ return *begin();
+ }
+
+ /// @brief access the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/front/
+ const_reference front() const
+ {
+ return *cbegin();
+ }
+
+ /// @brief access the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/back/
+ reference back()
+ {
+ auto tmp = end();
+ --tmp;
+ return *tmp;
+ }
+
+ /// @brief access the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/back/
+ const_reference back() const
+ {
+ auto tmp = cend();
+ --tmp;
+ return *tmp;
+ }
+
+ /// @brief remove element given an iterator
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ template < class IteratorType, detail::enable_if_t <
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+ IteratorType erase(IteratorType pos) // NOLINT(performance-unnecessary-value-param)
+ {
+ // make sure iterator fits the current value
+ if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ IteratorType result = end();
+
+ switch (m_data.m_type)
+ {
+ case value_t::boolean:
+ case value_t::number_float:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::string:
+ case value_t::binary:
+ {
+ if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
+ {
+ JSON_THROW(invalid_iterator::create(205, "iterator out of range", this));
+ }
+
+ if (is_string())
+ {
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);
+ m_data.m_value.string = nullptr;
+ }
+ else if (is_binary())
+ {
+ AllocatorType<binary_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);
+ m_data.m_value.binary = nullptr;
+ }
+
+ m_data.m_type = value_t::null;
+ assert_invariant();
+ break;
+ }
+
+ case value_t::object:
+ {
+ result.m_it.object_iterator = m_data.m_value.object->erase(pos.m_it.object_iterator);
+ break;
+ }
+
+ case value_t::array:
+ {
+ result.m_it.array_iterator = m_data.m_value.array->erase(pos.m_it.array_iterator);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+
+ return result;
+ }
+
+ /// @brief remove elements given an iterator range
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ template < class IteratorType, detail::enable_if_t <
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value ||
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int > = 0 >
+ IteratorType erase(IteratorType first, IteratorType last) // NOLINT(performance-unnecessary-value-param)
+ {
+ // make sure iterator fits the current value
+ if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", this));
+ }
+
+ IteratorType result = end();
+
+ switch (m_data.m_type)
+ {
+ case value_t::boolean:
+ case value_t::number_float:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::string:
+ case value_t::binary:
+ {
+ if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
+ || !last.m_it.primitive_iterator.is_end()))
+ {
+ JSON_THROW(invalid_iterator::create(204, "iterators out of range", this));
+ }
+
+ if (is_string())
+ {
+ AllocatorType<string_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.string);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.string, 1);
+ m_data.m_value.string = nullptr;
+ }
+ else if (is_binary())
+ {
+ AllocatorType<binary_t> alloc;
+ std::allocator_traits<decltype(alloc)>::destroy(alloc, m_data.m_value.binary);
+ std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_data.m_value.binary, 1);
+ m_data.m_value.binary = nullptr;
+ }
+
+ m_data.m_type = value_t::null;
+ assert_invariant();
+ break;
+ }
+
+ case value_t::object:
+ {
+ result.m_it.object_iterator = m_data.m_value.object->erase(first.m_it.object_iterator,
+ last.m_it.object_iterator);
+ break;
+ }
+
+ case value_t::array:
+ {
+ result.m_it.array_iterator = m_data.m_value.array->erase(first.m_it.array_iterator,
+ last.m_it.array_iterator);
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+
+ return result;
+ }
+
+ private:
+ template < typename KeyType, detail::enable_if_t <
+ detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+ size_type erase_internal(KeyType && key)
+ {
+ // this erase only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+
+ return m_data.m_value.object->erase(std::forward<KeyType>(key));
+ }
+
+ template < typename KeyType, detail::enable_if_t <
+ !detail::has_erase_with_key_type<basic_json_t, KeyType>::value, int > = 0 >
+ size_type erase_internal(KeyType && key)
+ {
+ // this erase only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+
+ const auto it = m_data.m_value.object->find(std::forward<KeyType>(key));
+ if (it != m_data.m_value.object->end())
+ {
+ m_data.m_value.object->erase(it);
+ return 1;
+ }
+ return 0;
+ }
+
+ public:
+
+ /// @brief remove element from a JSON object given a key
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ size_type erase(const typename object_t::key_type& key)
+ {
+ // the indirection via erase_internal() is added to avoid making this
+ // function a template and thus de-rank it during overload resolution
+ return erase_internal(key);
+ }
+
+ /// @brief remove element from a JSON object given a key
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ size_type erase(KeyType && key)
+ {
+ return erase_internal(std::forward<KeyType>(key));
+ }
+
+ /// @brief remove element from a JSON array given an index
+ /// @sa https://json.nlohmann.me/api/basic_json/erase/
+ void erase(const size_type idx)
+ {
+ // this erase only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ if (JSON_HEDLEY_UNLIKELY(idx >= size()))
+ {
+ JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), this));
+ }
+
+ m_data.m_value.array->erase(m_data.m_value.array->begin() + static_cast<difference_type>(idx));
+ }
+ else
+ {
+ JSON_THROW(type_error::create(307, detail::concat("cannot use erase() with ", type_name()), this));
+ }
+ }
+
+ /// @}
+
+ ////////////
+ // lookup //
+ ////////////
+
+ /// @name lookup
+ /// @{
+
+ /// @brief find an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/find/
+ iterator find(const typename object_t::key_type& key)
+ {
+ auto result = end();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_data.m_value.object->find(key);
+ }
+
+ return result;
+ }
+
+ /// @brief find an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/find/
+ const_iterator find(const typename object_t::key_type& key) const
+ {
+ auto result = cend();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_data.m_value.object->find(key);
+ }
+
+ return result;
+ }
+
+ /// @brief find an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/find/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ iterator find(KeyType && key)
+ {
+ auto result = end();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
+ }
+
+ return result;
+ }
+
+ /// @brief find an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/find/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ const_iterator find(KeyType && key) const
+ {
+ auto result = cend();
+
+ if (is_object())
+ {
+ result.m_it.object_iterator = m_data.m_value.object->find(std::forward<KeyType>(key));
+ }
+
+ return result;
+ }
+
+ /// @brief returns the number of occurrences of a key in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/count/
+ size_type count(const typename object_t::key_type& key) const
+ {
+ // return 0 for all nonobject types
+ return is_object() ? m_data.m_value.object->count(key) : 0;
+ }
+
+ /// @brief returns the number of occurrences of a key in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/count/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ size_type count(KeyType && key) const
+ {
+ // return 0 for all nonobject types
+ return is_object() ? m_data.m_value.object->count(std::forward<KeyType>(key)) : 0;
+ }
+
+ /// @brief check the existence of an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/contains/
+ bool contains(const typename object_t::key_type& key) const
+ {
+ return is_object() && m_data.m_value.object->find(key) != m_data.m_value.object->end();
+ }
+
+ /// @brief check the existence of an element in a JSON object
+ /// @sa https://json.nlohmann.me/api/basic_json/contains/
+ template<class KeyType, detail::enable_if_t<
+ detail::is_usable_as_basic_json_key_type<basic_json_t, KeyType>::value, int> = 0>
+ bool contains(KeyType && key) const
+ {
+ return is_object() && m_data.m_value.object->find(std::forward<KeyType>(key)) != m_data.m_value.object->end();
+ }
+
+ /// @brief check the existence of an element in a JSON object given a JSON pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/contains/
+ bool contains(const json_pointer& ptr) const
+ {
+ return ptr.contains(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ bool contains(const typename ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+ {
+ return ptr.contains(this);
+ }
+
+ /// @}
+
+ ///////////////
+ // iterators //
+ ///////////////
+
+ /// @name iterators
+ /// @{
+
+ /// @brief returns an iterator to the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/begin/
+ iterator begin() noexcept
+ {
+ iterator result(this);
+ result.set_begin();
+ return result;
+ }
+
+ /// @brief returns an iterator to the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/begin/
+ const_iterator begin() const noexcept
+ {
+ return cbegin();
+ }
+
+ /// @brief returns a const iterator to the first element
+ /// @sa https://json.nlohmann.me/api/basic_json/cbegin/
+ const_iterator cbegin() const noexcept
+ {
+ const_iterator result(this);
+ result.set_begin();
+ return result;
+ }
+
+ /// @brief returns an iterator to one past the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/end/
+ iterator end() noexcept
+ {
+ iterator result(this);
+ result.set_end();
+ return result;
+ }
+
+ /// @brief returns an iterator to one past the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/end/
+ const_iterator end() const noexcept
+ {
+ return cend();
+ }
+
+ /// @brief returns an iterator to one past the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/cend/
+ const_iterator cend() const noexcept
+ {
+ const_iterator result(this);
+ result.set_end();
+ return result;
+ }
+
+ /// @brief returns an iterator to the reverse-beginning
+ /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
+ reverse_iterator rbegin() noexcept
+ {
+ return reverse_iterator(end());
+ }
+
+ /// @brief returns an iterator to the reverse-beginning
+ /// @sa https://json.nlohmann.me/api/basic_json/rbegin/
+ const_reverse_iterator rbegin() const noexcept
+ {
+ return crbegin();
+ }
+
+ /// @brief returns an iterator to the reverse-end
+ /// @sa https://json.nlohmann.me/api/basic_json/rend/
+ reverse_iterator rend() noexcept
+ {
+ return reverse_iterator(begin());
+ }
+
+ /// @brief returns an iterator to the reverse-end
+ /// @sa https://json.nlohmann.me/api/basic_json/rend/
+ const_reverse_iterator rend() const noexcept
+ {
+ return crend();
+ }
+
+ /// @brief returns a const reverse iterator to the last element
+ /// @sa https://json.nlohmann.me/api/basic_json/crbegin/
+ const_reverse_iterator crbegin() const noexcept
+ {
+ return const_reverse_iterator(cend());
+ }
+
+ /// @brief returns a const reverse iterator to one before the first
+ /// @sa https://json.nlohmann.me/api/basic_json/crend/
+ const_reverse_iterator crend() const noexcept
+ {
+ return const_reverse_iterator(cbegin());
+ }
+
+ public:
+ /// @brief wrapper to access iterator member functions in range-based for
+ /// @sa https://json.nlohmann.me/api/basic_json/items/
+ /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+ /// version 4.0.0 of the library. Please use @ref items() instead;
+ /// that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+ JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+ static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept
+ {
+ return ref.items();
+ }
+
+ /// @brief wrapper to access iterator member functions in range-based for
+ /// @sa https://json.nlohmann.me/api/basic_json/items/
+ /// @deprecated This function is deprecated since 3.1.0 and will be removed in
+ /// version 4.0.0 of the library. Please use @ref items() instead;
+ /// that is, replace `json::iterator_wrapper(j)` with `j.items()`.
+ JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items())
+ static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept
+ {
+ return ref.items();
+ }
+
+ /// @brief helper to access iterator member functions in range-based for
+ /// @sa https://json.nlohmann.me/api/basic_json/items/
+ iteration_proxy<iterator> items() noexcept
+ {
+ return iteration_proxy<iterator>(*this);
+ }
+
+ /// @brief helper to access iterator member functions in range-based for
+ /// @sa https://json.nlohmann.me/api/basic_json/items/
+ iteration_proxy<const_iterator> items() const noexcept
+ {
+ return iteration_proxy<const_iterator>(*this);
+ }
+
+ /// @}
+
+ //////////////
+ // capacity //
+ //////////////
+
+ /// @name capacity
+ /// @{
+
+ /// @brief checks whether the container is empty.
+ /// @sa https://json.nlohmann.me/api/basic_json/empty/
+ bool empty() const noexcept
+ {
+ switch (m_data.m_type)
+ {
+ case value_t::null:
+ {
+ // null values are empty
+ return true;
+ }
+
+ case value_t::array:
+ {
+ // delegate call to array_t::empty()
+ return m_data.m_value.array->empty();
+ }
+
+ case value_t::object:
+ {
+ // delegate call to object_t::empty()
+ return m_data.m_value.object->empty();
+ }
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ // all other types are nonempty
+ return false;
+ }
+ }
+ }
+
+ /// @brief returns the number of elements
+ /// @sa https://json.nlohmann.me/api/basic_json/size/
+ size_type size() const noexcept
+ {
+ switch (m_data.m_type)
+ {
+ case value_t::null:
+ {
+ // null values are empty
+ return 0;
+ }
+
+ case value_t::array:
+ {
+ // delegate call to array_t::size()
+ return m_data.m_value.array->size();
+ }
+
+ case value_t::object:
+ {
+ // delegate call to object_t::size()
+ return m_data.m_value.object->size();
+ }
+
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ // all other types have size 1
+ return 1;
+ }
+ }
+ }
+
+ /// @brief returns the maximum possible number of elements
+ /// @sa https://json.nlohmann.me/api/basic_json/max_size/
+ size_type max_size() const noexcept
+ {
+ switch (m_data.m_type)
+ {
+ case value_t::array:
+ {
+ // delegate call to array_t::max_size()
+ return m_data.m_value.array->max_size();
+ }
+
+ case value_t::object:
+ {
+ // delegate call to object_t::max_size()
+ return m_data.m_value.object->max_size();
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ // all other types have max_size() == size()
+ return size();
+ }
+ }
+ }
+
+ /// @}
+
+ ///////////////
+ // modifiers //
+ ///////////////
+
+ /// @name modifiers
+ /// @{
+
+ /// @brief clears the contents
+ /// @sa https://json.nlohmann.me/api/basic_json/clear/
+ void clear() noexcept
+ {
+ switch (m_data.m_type)
+ {
+ case value_t::number_integer:
+ {
+ m_data.m_value.number_integer = 0;
+ break;
+ }
+
+ case value_t::number_unsigned:
+ {
+ m_data.m_value.number_unsigned = 0;
+ break;
+ }
+
+ case value_t::number_float:
+ {
+ m_data.m_value.number_float = 0.0;
+ break;
+ }
+
+ case value_t::boolean:
+ {
+ m_data.m_value.boolean = false;
+ break;
+ }
+
+ case value_t::string:
+ {
+ m_data.m_value.string->clear();
+ break;
+ }
+
+ case value_t::binary:
+ {
+ m_data.m_value.binary->clear();
+ break;
+ }
+
+ case value_t::array:
+ {
+ m_data.m_value.array->clear();
+ break;
+ }
+
+ case value_t::object:
+ {
+ m_data.m_value.object->clear();
+ break;
+ }
+
+ case value_t::null:
+ case value_t::discarded:
+ default:
+ break;
+ }
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+ void push_back(basic_json&& val)
+ {
+ // push_back only works for null objects or arrays
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+ {
+ JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_data.m_type = value_t::array;
+ m_data.m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array (move semantics)
+ const auto old_capacity = m_data.m_value.array->capacity();
+ m_data.m_value.array->push_back(std::move(val));
+ set_parent(m_data.m_value.array->back(), old_capacity);
+ // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+ reference operator+=(basic_json&& val)
+ {
+ push_back(std::move(val));
+ return *this;
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+ void push_back(const basic_json& val)
+ {
+ // push_back only works for null objects or arrays
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+ {
+ JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_data.m_type = value_t::array;
+ m_data.m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array
+ const auto old_capacity = m_data.m_value.array->capacity();
+ m_data.m_value.array->push_back(val);
+ set_parent(m_data.m_value.array->back(), old_capacity);
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+ reference operator+=(const basic_json& val)
+ {
+ push_back(val);
+ return *this;
+ }
+
+ /// @brief add an object to an object
+ /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+ void push_back(const typename object_t::value_type& val)
+ {
+ // push_back only works for null objects or objects
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
+ {
+ JSON_THROW(type_error::create(308, detail::concat("cannot use push_back() with ", type_name()), this));
+ }
+
+ // transform null object into an object
+ if (is_null())
+ {
+ m_data.m_type = value_t::object;
+ m_data.m_value = value_t::object;
+ assert_invariant();
+ }
+
+ // add element to object
+ auto res = m_data.m_value.object->insert(val);
+ set_parent(res.first->second);
+ }
+
+ /// @brief add an object to an object
+ /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+ reference operator+=(const typename object_t::value_type& val)
+ {
+ push_back(val);
+ return *this;
+ }
+
+ /// @brief add an object to an object
+ /// @sa https://json.nlohmann.me/api/basic_json/push_back/
+ void push_back(initializer_list_t init)
+ {
+ if (is_object() && init.size() == 2 && (*init.begin())->is_string())
+ {
+ basic_json&& key = init.begin()->moved_or_copied();
+ push_back(typename object_t::value_type(
+ std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));
+ }
+ else
+ {
+ push_back(basic_json(init));
+ }
+ }
+
+ /// @brief add an object to an object
+ /// @sa https://json.nlohmann.me/api/basic_json/operator+=/
+ reference operator+=(initializer_list_t init)
+ {
+ push_back(init);
+ return *this;
+ }
+
+ /// @brief add an object to an array
+ /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/
+ template<class... Args>
+ reference emplace_back(Args&& ... args)
+ {
+ // emplace_back only works for null objects or arrays
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
+ {
+ JSON_THROW(type_error::create(311, detail::concat("cannot use emplace_back() with ", type_name()), this));
+ }
+
+ // transform null object into an array
+ if (is_null())
+ {
+ m_data.m_type = value_t::array;
+ m_data.m_value = value_t::array;
+ assert_invariant();
+ }
+
+ // add element to array (perfect forwarding)
+ const auto old_capacity = m_data.m_value.array->capacity();
+ m_data.m_value.array->emplace_back(std::forward<Args>(args)...);
+ return set_parent(m_data.m_value.array->back(), old_capacity);
+ }
+
+ /// @brief add an object to an object if key does not exist
+ /// @sa https://json.nlohmann.me/api/basic_json/emplace/
+ template<class... Args>
+ std::pair<iterator, bool> emplace(Args&& ... args)
+ {
+ // emplace only works for null objects or arrays
+ if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
+ {
+ JSON_THROW(type_error::create(311, detail::concat("cannot use emplace() with ", type_name()), this));
+ }
+
+ // transform null object into an object
+ if (is_null())
+ {
+ m_data.m_type = value_t::object;
+ m_data.m_value = value_t::object;
+ assert_invariant();
+ }
+
+ // add element to array (perfect forwarding)
+ auto res = m_data.m_value.object->emplace(std::forward<Args>(args)...);
+ set_parent(res.first->second);
+
+ // create result iterator and set iterator to the result of emplace
+ auto it = begin();
+ it.m_it.object_iterator = res.first;
+
+ // return pair of iterator and boolean
+ return {it, res.second};
+ }
+
+ /// Helper for insertion of an iterator
+ /// @note: This uses std::distance to support GCC 4.8,
+ /// see https://github.com/nlohmann/json/pull/1257
+ template<typename... Args>
+ iterator insert_iterator(const_iterator pos, Args&& ... args) // NOLINT(performance-unnecessary-value-param)
+ {
+ iterator result(this);
+ JSON_ASSERT(m_data.m_value.array != nullptr);
+
+ auto insert_pos = std::distance(m_data.m_value.array->begin(), pos.m_it.array_iterator);
+ m_data.m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);
+ result.m_it.array_iterator = m_data.m_value.array->begin() + insert_pos;
+
+ // This could have been written as:
+ // result.m_it.array_iterator = m_data.m_value.array->insert(pos.m_it.array_iterator, cnt, val);
+ // but the return value of insert is missing in GCC 4.8, so it is written this way instead.
+
+ set_parents();
+ return result;
+ }
+
+ /// @brief inserts element into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
+ {
+ // insert only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ // check if iterator pos fits to this JSON value
+ if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ // insert to array and return iterator
+ return insert_iterator(pos, val);
+ }
+
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ /// @brief inserts element into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, basic_json&& val) // NOLINT(performance-unnecessary-value-param)
+ {
+ return insert(pos, val);
+ }
+
+ /// @brief inserts copies of element into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, size_type cnt, const basic_json& val) // NOLINT(performance-unnecessary-value-param)
+ {
+ // insert only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ // check if iterator pos fits to this JSON value
+ if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ // insert to array and return iterator
+ return insert_iterator(pos, cnt, val);
+ }
+
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ /// @brief inserts range of elements into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param)
+ {
+ // insert only works for arrays
+ if (JSON_HEDLEY_UNLIKELY(!is_array()))
+ {
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ // check if iterator pos fits to this JSON value
+ if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
+ {
+ JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", this));
+ }
+
+ // insert to array and return iterator
+ return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
+ }
+
+ /// @brief inserts elements from initializer list into array
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ iterator insert(const_iterator pos, initializer_list_t ilist) // NOLINT(performance-unnecessary-value-param)
+ {
+ // insert only works for arrays
+ if (JSON_HEDLEY_UNLIKELY(!is_array()))
+ {
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ // check if iterator pos fits to this JSON value
+ if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", this));
+ }
+
+ // insert to array and return iterator
+ return insert_iterator(pos, ilist.begin(), ilist.end());
+ }
+
+ /// @brief inserts range of elements into object
+ /// @sa https://json.nlohmann.me/api/basic_json/insert/
+ void insert(const_iterator first, const_iterator last) // NOLINT(performance-unnecessary-value-param)
+ {
+ // insert only works for objects
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(309, detail::concat("cannot use insert() with ", type_name()), this));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+ {
+ JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", this));
+ }
+
+ m_data.m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
+ set_parents();
+ }
+
+ /// @brief updates a JSON object from another object, overwriting existing keys
+ /// @sa https://json.nlohmann.me/api/basic_json/update/
+ void update(const_reference j, bool merge_objects = false)
+ {
+ update(j.begin(), j.end(), merge_objects);
+ }
+
+ /// @brief updates a JSON object from another object, overwriting existing keys
+ /// @sa https://json.nlohmann.me/api/basic_json/update/
+ void update(const_iterator first, const_iterator last, bool merge_objects = false) // NOLINT(performance-unnecessary-value-param)
+ {
+ // implicitly convert null value to an empty object
+ if (is_null())
+ {
+ m_data.m_type = value_t::object;
+ m_data.m_value.object = create<object_t>();
+ assert_invariant();
+ }
+
+ if (JSON_HEDLEY_UNLIKELY(!is_object()))
+ {
+ JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", type_name()), this));
+ }
+
+ // check if range iterators belong to the same JSON object
+ if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
+ {
+ JSON_THROW(invalid_iterator::create(210, "iterators do not fit", this));
+ }
+
+ // passed iterators must belong to objects
+ if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
+ {
+ JSON_THROW(type_error::create(312, detail::concat("cannot use update() with ", first.m_object->type_name()), first.m_object));
+ }
+
+ for (auto it = first; it != last; ++it)
+ {
+ if (merge_objects && it.value().is_object())
+ {
+ auto it2 = m_data.m_value.object->find(it.key());
+ if (it2 != m_data.m_value.object->end())
+ {
+ it2->second.update(it.value(), true);
+ continue;
+ }
+ }
+ m_data.m_value.object->operator[](it.key()) = it.value();
+#if JSON_DIAGNOSTICS
+ m_data.m_value.object->operator[](it.key()).m_parent = this;
+#endif
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(reference other) noexcept (
+ std::is_nothrow_move_constructible<value_t>::value&&
+ std::is_nothrow_move_assignable<value_t>::value&&
+ std::is_nothrow_move_constructible<json_value>::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
+ std::is_nothrow_move_assignable<json_value>::value
+ )
+ {
+ std::swap(m_data.m_type, other.m_data.m_type);
+ std::swap(m_data.m_value, other.m_data.m_value);
+
+ set_parents();
+ other.set_parents();
+ assert_invariant();
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ friend void swap(reference left, reference right) noexcept (
+ std::is_nothrow_move_constructible<value_t>::value&&
+ std::is_nothrow_move_assignable<value_t>::value&&
+ std::is_nothrow_move_constructible<json_value>::value&& // NOLINT(cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
+ std::is_nothrow_move_assignable<json_value>::value
+ )
+ {
+ left.swap(right);
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(array_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
+ {
+ // swap only works for arrays
+ if (JSON_HEDLEY_LIKELY(is_array()))
+ {
+ using std::swap;
+ swap(*(m_data.m_value.array), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(array_t&) with ", type_name()), this));
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(object_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
+ {
+ // swap only works for objects
+ if (JSON_HEDLEY_LIKELY(is_object()))
+ {
+ using std::swap;
+ swap(*(m_data.m_value.object), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(object_t&) with ", type_name()), this));
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(string_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
+ {
+ // swap only works for strings
+ if (JSON_HEDLEY_LIKELY(is_string()))
+ {
+ using std::swap;
+ swap(*(m_data.m_value.string), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(string_t&) with ", type_name()), this));
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(binary_t& other) // NOLINT(bugprone-exception-escape,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
+ {
+ // swap only works for strings
+ if (JSON_HEDLEY_LIKELY(is_binary()))
+ {
+ using std::swap;
+ swap(*(m_data.m_value.binary), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t&) with ", type_name()), this));
+ }
+ }
+
+ /// @brief exchanges the values
+ /// @sa https://json.nlohmann.me/api/basic_json/swap/
+ void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape)
+ {
+ // swap only works for strings
+ if (JSON_HEDLEY_LIKELY(is_binary()))
+ {
+ using std::swap;
+ swap(*(m_data.m_value.binary), other);
+ }
+ else
+ {
+ JSON_THROW(type_error::create(310, detail::concat("cannot use swap(binary_t::container_type&) with ", type_name()), this));
+ }
+ }
+
+ /// @}
+
+ //////////////////////////////////////////
+ // lexicographical comparison operators //
+ //////////////////////////////////////////
+
+ /// @name lexicographical comparison operators
+ /// @{
+
+ // note parentheses around operands are necessary; see
+ // https://github.com/nlohmann/json/issues/1530
+#define JSON_IMPLEMENT_OPERATOR(op, null_result, unordered_result, default_result) \
+ const auto lhs_type = lhs.type(); \
+ const auto rhs_type = rhs.type(); \
+ \
+ if (lhs_type == rhs_type) /* NOLINT(readability/braces) */ \
+ { \
+ switch (lhs_type) \
+ { \
+ case value_t::array: \
+ return (*lhs.m_data.m_value.array) op (*rhs.m_data.m_value.array); \
+ \
+ case value_t::object: \
+ return (*lhs.m_data.m_value.object) op (*rhs.m_data.m_value.object); \
+ \
+ case value_t::null: \
+ return (null_result); \
+ \
+ case value_t::string: \
+ return (*lhs.m_data.m_value.string) op (*rhs.m_data.m_value.string); \
+ \
+ case value_t::boolean: \
+ return (lhs.m_data.m_value.boolean) op (rhs.m_data.m_value.boolean); \
+ \
+ case value_t::number_integer: \
+ return (lhs.m_data.m_value.number_integer) op (rhs.m_data.m_value.number_integer); \
+ \
+ case value_t::number_unsigned: \
+ return (lhs.m_data.m_value.number_unsigned) op (rhs.m_data.m_value.number_unsigned); \
+ \
+ case value_t::number_float: \
+ return (lhs.m_data.m_value.number_float) op (rhs.m_data.m_value.number_float); \
+ \
+ case value_t::binary: \
+ return (*lhs.m_data.m_value.binary) op (*rhs.m_data.m_value.binary); \
+ \
+ case value_t::discarded: \
+ default: \
+ return (unordered_result); \
+ } \
+ } \
+ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) \
+ { \
+ return static_cast<number_float_t>(lhs.m_data.m_value.number_integer) op rhs.m_data.m_value.number_float; \
+ } \
+ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) \
+ { \
+ return lhs.m_data.m_value.number_float op static_cast<number_float_t>(rhs.m_data.m_value.number_integer); \
+ } \
+ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) \
+ { \
+ return static_cast<number_float_t>(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_float; \
+ } \
+ else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) \
+ { \
+ return lhs.m_data.m_value.number_float op static_cast<number_float_t>(rhs.m_data.m_value.number_unsigned); \
+ } \
+ else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) \
+ { \
+ return static_cast<number_integer_t>(lhs.m_data.m_value.number_unsigned) op rhs.m_data.m_value.number_integer; \
+ } \
+ else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) \
+ { \
+ return lhs.m_data.m_value.number_integer op static_cast<number_integer_t>(rhs.m_data.m_value.number_unsigned); \
+ } \
+ else if(compares_unordered(lhs, rhs))\
+ {\
+ return (unordered_result);\
+ }\
+ \
+ return (default_result);
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ // returns true if:
+ // - any operand is NaN and the other operand is of number type
+ // - any operand is discarded
+ // in legacy mode, discarded values are considered ordered if
+ // an operation is computed as an odd number of inverses of others
+ static bool compares_unordered(const_reference lhs, const_reference rhs, bool inverse = false) noexcept
+ {
+ if ((lhs.is_number_float() && std::isnan(lhs.m_data.m_value.number_float) && rhs.is_number())
+ || (rhs.is_number_float() && std::isnan(rhs.m_data.m_value.number_float) && lhs.is_number()))
+ {
+ return true;
+ }
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ return (lhs.is_discarded() || rhs.is_discarded()) && !inverse;
+#else
+ static_cast<void>(inverse);
+ return lhs.is_discarded() || rhs.is_discarded();
+#endif
+ }
+
+ private:
+ bool compares_unordered(const_reference rhs, bool inverse = false) const noexcept
+ {
+ return compares_unordered(*this, rhs, inverse);
+ }
+
+ public:
+#if JSON_HAS_THREE_WAY_COMPARISON
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ bool operator==(const_reference rhs) const noexcept
+ {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ const_reference lhs = *this;
+ JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ }
+
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ template<typename ScalarType>
+ requires std::is_scalar_v<ScalarType>
+ bool operator==(ScalarType rhs) const noexcept
+ {
+ return *this == basic_json(rhs);
+ }
+
+ /// @brief comparison: not equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+ bool operator!=(const_reference rhs) const noexcept
+ {
+ if (compares_unordered(rhs, true))
+ {
+ return false;
+ }
+ return !operator==(rhs);
+ }
+
+ /// @brief comparison: 3-way
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+ std::partial_ordering operator<=>(const_reference rhs) const noexcept // *NOPAD*
+ {
+ const_reference lhs = *this;
+ // default_result is used if we cannot compare values. In that case,
+ // we compare types.
+ JSON_IMPLEMENT_OPERATOR(<=>, // *NOPAD*
+ std::partial_ordering::equivalent,
+ std::partial_ordering::unordered,
+ lhs_type <=> rhs_type) // *NOPAD*
+ }
+
+ /// @brief comparison: 3-way
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_spaceship/
+ template<typename ScalarType>
+ requires std::is_scalar_v<ScalarType>
+ std::partial_ordering operator<=>(ScalarType rhs) const noexcept // *NOPAD*
+ {
+ return *this <=> basic_json(rhs); // *NOPAD*
+ }
+
+#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+ // all operators that are computed as an odd number of inverses of others
+ // need to be overloaded to emulate the legacy comparison behavior
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+ bool operator<=(const_reference rhs) const noexcept
+ {
+ if (compares_unordered(rhs, true))
+ {
+ return false;
+ }
+ return !(rhs < *this);
+ }
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ template<typename ScalarType>
+ requires std::is_scalar_v<ScalarType>
+ bool operator<=(ScalarType rhs) const noexcept
+ {
+ return *this <= basic_json(rhs);
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON)
+ bool operator>=(const_reference rhs) const noexcept
+ {
+ if (compares_unordered(rhs, true))
+ {
+ return false;
+ }
+ return !(*this < rhs);
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ template<typename ScalarType>
+ requires std::is_scalar_v<ScalarType>
+ bool operator>=(ScalarType rhs) const noexcept
+ {
+ return *this >= basic_json(rhs);
+ }
+#endif
+#else
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ friend bool operator==(const_reference lhs, const_reference rhs) noexcept
+ {
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wfloat-equal"
+#endif
+ JSON_IMPLEMENT_OPERATOR( ==, true, false, false)
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ }
+
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs == basic_json(rhs);
+ }
+
+ /// @brief comparison: equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator==(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) == rhs;
+ }
+
+ /// @brief comparison: not equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+ friend bool operator!=(const_reference lhs, const_reference rhs) noexcept
+ {
+ if (compares_unordered(lhs, rhs, true))
+ {
+ return false;
+ }
+ return !(lhs == rhs);
+ }
+
+ /// @brief comparison: not equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs != basic_json(rhs);
+ }
+
+ /// @brief comparison: not equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) != rhs;
+ }
+
+ /// @brief comparison: less than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+ friend bool operator<(const_reference lhs, const_reference rhs) noexcept
+ {
+ // default_result is used if we cannot compare values. In that case,
+ // we compare types. Note we have to call the operator explicitly,
+ // because MSVC has problems otherwise.
+ JSON_IMPLEMENT_OPERATOR( <, false, false, operator<(lhs_type, rhs_type))
+ }
+
+ /// @brief comparison: less than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs < basic_json(rhs);
+ }
+
+ /// @brief comparison: less than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) < rhs;
+ }
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ friend bool operator<=(const_reference lhs, const_reference rhs) noexcept
+ {
+ if (compares_unordered(lhs, rhs, true))
+ {
+ return false;
+ }
+ return !(rhs < lhs);
+ }
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs <= basic_json(rhs);
+ }
+
+ /// @brief comparison: less than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_le/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) <= rhs;
+ }
+
+ /// @brief comparison: greater than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+ friend bool operator>(const_reference lhs, const_reference rhs) noexcept
+ {
+ // double inverse
+ if (compares_unordered(lhs, rhs))
+ {
+ return false;
+ }
+ return !(lhs <= rhs);
+ }
+
+ /// @brief comparison: greater than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs > basic_json(rhs);
+ }
+
+ /// @brief comparison: greater than
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) > rhs;
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ friend bool operator>=(const_reference lhs, const_reference rhs) noexcept
+ {
+ if (compares_unordered(lhs, rhs, true))
+ {
+ return false;
+ }
+ return !(lhs < rhs);
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept
+ {
+ return lhs >= basic_json(rhs);
+ }
+
+ /// @brief comparison: greater than or equal
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
+ template<typename ScalarType, typename std::enable_if<
+ std::is_scalar<ScalarType>::value, int>::type = 0>
+ friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
+ {
+ return basic_json(lhs) >= rhs;
+ }
+#endif
+
+#undef JSON_IMPLEMENT_OPERATOR
+
+ /// @}
+
+ ///////////////////
+ // serialization //
+ ///////////////////
+
+ /// @name serialization
+ /// @{
+#ifndef JSON_NO_IO
+ /// @brief serialize to stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+ friend std::ostream& operator<<(std::ostream& o, const basic_json& j)
+ {
+ // read width member and use it as indentation parameter if nonzero
+ const bool pretty_print = o.width() > 0;
+ const auto indentation = pretty_print ? o.width() : 0;
+
+ // reset width to 0 for subsequent calls to this stream
+ o.width(0);
+
+ // do the actual serialization
+ serializer s(detail::output_adapter<char>(o), o.fill());
+ s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));
+ return o;
+ }
+
+ /// @brief serialize to stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/
+ /// @deprecated This function is deprecated since 3.0.0 and will be removed in
+ /// version 4.0.0 of the library. Please use
+ /// operator<<(std::ostream&, const basic_json&) instead; that is,
+ /// replace calls like `j >> o;` with `o << j;`.
+ JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&))
+ friend std::ostream& operator>>(const basic_json& j, std::ostream& o)
+ {
+ return o << j;
+ }
+#endif // JSON_NO_IO
+ /// @}
+
+ /////////////////////
+ // deserialization //
+ /////////////////////
+
+ /// @name deserialization
+ /// @{
+
+ /// @brief deserialize from a compatible input
+ /// @sa https://json.nlohmann.me/api/basic_json/parse/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json parse(InputType&& i,
+ parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false)
+ {
+ basic_json result;
+ parser(detail::input_adapter(std::forward<InputType>(i)), std::move(cb), allow_exceptions, ignore_comments).parse(true, result); // cppcheck-suppress[accessMoved,accessForwarded]
+ return result;
+ }
+
+ /// @brief deserialize from a pair of character iterators
+ /// @sa https://json.nlohmann.me/api/basic_json/parse/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json parse(IteratorType first,
+ IteratorType last,
+ parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false)
+ {
+ basic_json result;
+ parser(detail::input_adapter(std::move(first), std::move(last)), std::move(cb), allow_exceptions, ignore_comments).parse(true, result); // cppcheck-suppress[accessMoved]
+ return result;
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len))
+ static basic_json parse(detail::span_input_adapter&& i,
+ parser_callback_t cb = nullptr,
+ const bool allow_exceptions = true,
+ const bool ignore_comments = false)
+ {
+ basic_json result;
+ parser(i.get(), std::move(cb), allow_exceptions, ignore_comments).parse(true, result); // cppcheck-suppress[accessMoved]
+ return result;
+ }
+
+ /// @brief check if the input is valid JSON
+ /// @sa https://json.nlohmann.me/api/basic_json/accept/
+ template<typename InputType>
+ static bool accept(InputType&& i,
+ const bool ignore_comments = false)
+ {
+ return parser(detail::input_adapter(std::forward<InputType>(i)), nullptr, false, ignore_comments).accept(true);
+ }
+
+ /// @brief check if the input is valid JSON
+ /// @sa https://json.nlohmann.me/api/basic_json/accept/
+ template<typename IteratorType>
+ static bool accept(IteratorType first, IteratorType last,
+ const bool ignore_comments = false)
+ {
+ return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true);
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len))
+ static bool accept(detail::span_input_adapter&& i,
+ const bool ignore_comments = false)
+ {
+ return parser(i.get(), nullptr, false, ignore_comments).accept(true);
+ }
+
+ /// @brief generate SAX events
+ /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+ template <typename InputType, typename SAX>
+ JSON_HEDLEY_NON_NULL(2)
+ static bool sax_parse(InputType&& i, SAX* sax,
+ input_format_t format = input_format_t::json,
+ const bool strict = true,
+ const bool ignore_comments = false)
+ {
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ return format == input_format_t::json
+ ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+ : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+ }
+
+ /// @brief generate SAX events
+ /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+ template<class IteratorType, class SAX>
+ JSON_HEDLEY_NON_NULL(3)
+ static bool sax_parse(IteratorType first, IteratorType last, SAX* sax,
+ input_format_t format = input_format_t::json,
+ const bool strict = true,
+ const bool ignore_comments = false)
+ {
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ return format == input_format_t::json
+ ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+ : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+ }
+
+ /// @brief generate SAX events
+ /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/
+ /// @deprecated This function is deprecated since 3.8.0 and will be removed in
+ /// version 4.0.0 of the library. Please use
+ /// sax_parse(ptr, ptr + len) instead.
+ template <typename SAX>
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...))
+ JSON_HEDLEY_NON_NULL(2)
+ static bool sax_parse(detail::span_input_adapter&& i, SAX* sax,
+ input_format_t format = input_format_t::json,
+ const bool strict = true,
+ const bool ignore_comments = false)
+ {
+ auto ia = i.get();
+ return format == input_format_t::json
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict)
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ : detail::binary_reader<basic_json, decltype(ia), SAX>(std::move(ia), format).sax_parse(format, sax, strict);
+ }
+#ifndef JSON_NO_IO
+ /// @brief deserialize from stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+ /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in
+ /// version 4.0.0 of the library. Please use
+ /// operator>>(std::istream&, basic_json&) instead; that is,
+ /// replace calls like `j << i;` with `i >> j;`.
+ JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&))
+ friend std::istream& operator<<(basic_json& j, std::istream& i)
+ {
+ return operator>>(i, j);
+ }
+
+ /// @brief deserialize from stream
+ /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
+ friend std::istream& operator>>(std::istream& i, basic_json& j)
+ {
+ parser(detail::input_adapter(i)).parse(false, j);
+ return i;
+ }
+#endif // JSON_NO_IO
+ /// @}
+
+ ///////////////////////////
+ // convenience functions //
+ ///////////////////////////
+
+ /// @brief return the type as string
+ /// @sa https://json.nlohmann.me/api/basic_json/type_name/
+ JSON_HEDLEY_RETURNS_NON_NULL
+ const char* type_name() const noexcept
+ {
+ switch (m_data.m_type)
+ {
+ case value_t::null:
+ return "null";
+ case value_t::object:
+ return "object";
+ case value_t::array:
+ return "array";
+ case value_t::string:
+ return "string";
+ case value_t::boolean:
+ return "boolean";
+ case value_t::binary:
+ return "binary";
+ case value_t::discarded:
+ return "discarded";
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ default:
+ return "number";
+ }
+ }
+
+ JSON_PRIVATE_UNLESS_TESTED:
+ //////////////////////
+ // member variables //
+ //////////////////////
+
+ struct data
+ {
+ /// the type of the current element
+ value_t m_type = value_t::null;
+
+ /// the value of the current element
+ json_value m_value = {};
+
+ data(const value_t v)
+ : m_type(v), m_value(v)
+ {
+ }
+
+ data(size_type cnt, const basic_json& val)
+ : m_type(value_t::array)
+ {
+ m_value.array = create<array_t>(cnt, val);
+ }
+
+ data() noexcept = default;
+ data(data&&) noexcept = default;
+ data(const data&) noexcept = delete;
+ data& operator=(data&&) noexcept = delete;
+ data& operator=(const data&) noexcept = delete;
+
+ ~data() noexcept
+ {
+ m_value.destroy(m_type);
+ }
+ };
+
+ data m_data = {};
+
+#if JSON_DIAGNOSTICS
+ /// a pointer to a parent value (for debugging purposes)
+ basic_json* m_parent = nullptr;
+#endif
+
+#if JSON_DIAGNOSTIC_POSITIONS
+ /// the start position of the value
+ std::size_t start_position = std::string::npos;
+ /// the end position of the value
+ std::size_t end_position = std::string::npos;
+ public:
+ constexpr std::size_t start_pos() const noexcept
+ {
+ return start_position;
+ }
+
+ constexpr std::size_t end_pos() const noexcept
+ {
+ return end_position;
+ }
+#endif
+
+ //////////////////////////////////////////
+ // binary serialization/deserialization //
+ //////////////////////////////////////////
+
+ /// @name binary serialization/deserialization support
+ /// @{
+
+ public:
+ /// @brief create a CBOR serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+ static std::vector<std::uint8_t> to_cbor(const basic_json& j)
+ {
+ std::vector<std::uint8_t> result;
+ to_cbor(j, result);
+ return result;
+ }
+
+ /// @brief create a CBOR serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+ static void to_cbor(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+ {
+ binary_writer<std::uint8_t>(o).write_cbor(j);
+ }
+
+ /// @brief create a CBOR serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/
+ static void to_cbor(const basic_json& j, detail::output_adapter<char> o)
+ {
+ binary_writer<char>(o).write_cbor(j);
+ }
+
+ /// @brief create a MessagePack serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+ static std::vector<std::uint8_t> to_msgpack(const basic_json& j)
+ {
+ std::vector<std::uint8_t> result;
+ to_msgpack(j, result);
+ return result;
+ }
+
+ /// @brief create a MessagePack serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+ static void to_msgpack(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+ {
+ binary_writer<std::uint8_t>(o).write_msgpack(j);
+ }
+
+ /// @brief create a MessagePack serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/
+ static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)
+ {
+ binary_writer<char>(o).write_msgpack(j);
+ }
+
+ /// @brief create a UBJSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+ static std::vector<std::uint8_t> to_ubjson(const basic_json& j,
+ const bool use_size = false,
+ const bool use_type = false)
+ {
+ std::vector<std::uint8_t> result;
+ to_ubjson(j, result, use_size, use_type);
+ return result;
+ }
+
+ /// @brief create a UBJSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+ static void to_ubjson(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+ const bool use_size = false, const bool use_type = false)
+ {
+ binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type);
+ }
+
+ /// @brief create a UBJSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/
+ static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,
+ const bool use_size = false, const bool use_type = false)
+ {
+ binary_writer<char>(o).write_ubjson(j, use_size, use_type);
+ }
+
+ /// @brief create a BJData serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+ static std::vector<std::uint8_t> to_bjdata(const basic_json& j,
+ const bool use_size = false,
+ const bool use_type = false,
+ const bjdata_version_t version = bjdata_version_t::draft2)
+ {
+ std::vector<std::uint8_t> result;
+ to_bjdata(j, result, use_size, use_type, version);
+ return result;
+ }
+
+ /// @brief create a BJData serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+ static void to_bjdata(const basic_json& j, detail::output_adapter<std::uint8_t> o,
+ const bool use_size = false, const bool use_type = false,
+ const bjdata_version_t version = bjdata_version_t::draft2)
+ {
+ binary_writer<std::uint8_t>(o).write_ubjson(j, use_size, use_type, true, true, version);
+ }
+
+ /// @brief create a BJData serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bjdata/
+ static void to_bjdata(const basic_json& j, detail::output_adapter<char> o,
+ const bool use_size = false, const bool use_type = false,
+ const bjdata_version_t version = bjdata_version_t::draft2)
+ {
+ binary_writer<char>(o).write_ubjson(j, use_size, use_type, true, true, version);
+ }
+
+ /// @brief create a BSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+ static std::vector<std::uint8_t> to_bson(const basic_json& j)
+ {
+ std::vector<std::uint8_t> result;
+ to_bson(j, result);
+ return result;
+ }
+
+ /// @brief create a BSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+ static void to_bson(const basic_json& j, detail::output_adapter<std::uint8_t> o)
+ {
+ binary_writer<std::uint8_t>(o).write_bson(j);
+ }
+
+ /// @brief create a BSON serialization of a given JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/to_bson/
+ static void to_bson(const basic_json& j, detail::output_adapter<char> o)
+ {
+ binary_writer<char>(o).write_bson(j);
+ }
+
+ /// @brief create a JSON value from an input in CBOR format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_cbor(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in CBOR format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_cbor(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ template<typename T>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+ static basic_json from_cbor(const T* ptr, std::size_t len,
+ const bool strict = true,
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler);
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len))
+ static basic_json from_cbor(detail::span_input_adapter&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true,
+ const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error)
+ {
+ basic_json result;
+ auto ia = i.get();
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::cbor).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in MessagePack format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_msgpack(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in MessagePack format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_msgpack(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ template<typename T>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+ static basic_json from_msgpack(const T* ptr, std::size_t len,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ return from_msgpack(ptr, ptr + len, strict, allow_exceptions);
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len))
+ static basic_json from_msgpack(detail::span_input_adapter&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = i.get();
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::msgpack).sax_parse(input_format_t::msgpack, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in UBJSON format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_ubjson(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in UBJSON format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_ubjson(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ template<typename T>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+ static basic_json from_ubjson(const T* ptr, std::size_t len,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ return from_ubjson(ptr, ptr + len, strict, allow_exceptions);
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len))
+ static basic_json from_ubjson(detail::span_input_adapter&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = i.get();
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::ubjson).sax_parse(input_format_t::ubjson, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in BJData format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_bjdata(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in BJData format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_bjdata/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_bjdata(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bjdata).sax_parse(input_format_t::bjdata, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in BSON format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
+ template<typename InputType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_bson(InputType&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::forward<InputType>(i));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ /// @brief create a JSON value from an input in BSON format
+ /// @sa https://json.nlohmann.me/api/basic_json/from_bson/
+ template<typename IteratorType>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json from_bson(IteratorType first, IteratorType last,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = detail::input_adapter(std::move(first), std::move(last));
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+
+ template<typename T>
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+ static basic_json from_bson(const T* ptr, std::size_t len,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ return from_bson(ptr, ptr + len, strict, allow_exceptions);
+ }
+
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len))
+ static basic_json from_bson(detail::span_input_adapter&& i,
+ const bool strict = true,
+ const bool allow_exceptions = true)
+ {
+ basic_json result;
+ auto ia = i.get();
+ detail::json_sax_dom_parser<basic_json, decltype(ia)> sdp(result, allow_exceptions);
+ // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+ const bool res = binary_reader<decltype(ia)>(std::move(ia), input_format_t::bson).sax_parse(input_format_t::bson, &sdp, strict); // cppcheck-suppress[accessMoved]
+ return res ? result : basic_json(value_t::discarded);
+ }
+ /// @}
+
+ //////////////////////////
+ // JSON Pointer support //
+ //////////////////////////
+
+ /// @name JSON Pointer functions
+ /// @{
+
+ /// @brief access specified element via JSON Pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ reference operator[](const json_pointer& ptr)
+ {
+ return ptr.get_unchecked(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr)
+ {
+ return ptr.get_unchecked(this);
+ }
+
+ /// @brief access specified element via JSON Pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/
+ const_reference operator[](const json_pointer& ptr) const
+ {
+ return ptr.get_unchecked(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ const_reference operator[](const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+ {
+ return ptr.get_unchecked(this);
+ }
+
+ /// @brief access specified element via JSON Pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ reference at(const json_pointer& ptr)
+ {
+ return ptr.get_checked(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr)
+ {
+ return ptr.get_checked(this);
+ }
+
+ /// @brief access specified element via JSON Pointer
+ /// @sa https://json.nlohmann.me/api/basic_json/at/
+ const_reference at(const json_pointer& ptr) const
+ {
+ return ptr.get_checked(this);
+ }
+
+ template<typename BasicJsonType, detail::enable_if_t<detail::is_basic_json<BasicJsonType>::value, int> = 0>
+ JSON_HEDLEY_DEPRECATED_FOR(3.11.0, basic_json::json_pointer or nlohmann::json_pointer<basic_json::string_t>) // NOLINT(readability/alt_tokens)
+ const_reference at(const ::nlohmann::json_pointer<BasicJsonType>& ptr) const
+ {
+ return ptr.get_checked(this);
+ }
+
+ /// @brief return flattened JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/flatten/
+ basic_json flatten() const
+ {
+ basic_json result(value_t::object);
+ json_pointer::flatten("", *this, result);
+ return result;
+ }
+
+ /// @brief unflatten a previously flattened JSON value
+ /// @sa https://json.nlohmann.me/api/basic_json/unflatten/
+ basic_json unflatten() const
+ {
+ return json_pointer::unflatten(*this);
+ }
+
+ /// @}
+
+ //////////////////////////
+ // JSON Patch functions //
+ //////////////////////////
+
+ /// @name JSON Patch functions
+ /// @{
+
+ /// @brief applies a JSON patch in-place without copying the object
+ /// @sa https://json.nlohmann.me/api/basic_json/patch/
+ void patch_inplace(const basic_json& json_patch)
+ {
+ basic_json& result = *this;
+ // the valid JSON Patch operations
+ enum class patch_operations {add, remove, replace, move, copy, test, invalid};
+
+ const auto get_op = [](const string_t& op)
+ {
+ if (op == "add")
+ {
+ return patch_operations::add;
+ }
+ if (op == "remove")
+ {
+ return patch_operations::remove;
+ }
+ if (op == "replace")
+ {
+ return patch_operations::replace;
+ }
+ if (op == "move")
+ {
+ return patch_operations::move;
+ }
+ if (op == "copy")
+ {
+ return patch_operations::copy;
+ }
+ if (op == "test")
+ {
+ return patch_operations::test;
+ }
+
+ return patch_operations::invalid;
+ };
+
+ // wrapper for "add" operation; add value at ptr
+ const auto operation_add = [&result](json_pointer & ptr, const basic_json & val)
+ {
+ // adding to the root of the target document means replacing it
+ if (ptr.empty())
+ {
+ result = val;
+ return;
+ }
+
+ // make sure the top element of the pointer exists
+ json_pointer const top_pointer = ptr.top();
+ if (top_pointer != ptr)
+ {
+ result.at(top_pointer);
+ }
+
+ // get reference to parent of JSON pointer ptr
+ const auto last_path = ptr.back();
+ ptr.pop_back();
+ // parent must exist when performing patch add per RFC6902 specs
+ basic_json& parent = result.at(ptr);
+
+ switch (parent.m_data.m_type)
+ {
+ case value_t::null:
+ case value_t::object:
+ {
+ // use operator[] to add value
+ parent[last_path] = val;
+ break;
+ }
+
+ case value_t::array:
+ {
+ if (last_path == "-")
+ {
+ // special case: append to back
+ parent.push_back(val);
+ }
+ else
+ {
+ const auto idx = json_pointer::template array_index<basic_json_t>(last_path);
+ if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
+ {
+ // avoid undefined behavior
+ JSON_THROW(out_of_range::create(401, detail::concat("array index ", std::to_string(idx), " is out of range"), &parent));
+ }
+
+ // default case: insert add offset
+ parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
+ }
+ break;
+ }
+
+ // if there exists a parent it cannot be primitive
+ case value_t::string: // LCOV_EXCL_LINE
+ case value_t::boolean: // LCOV_EXCL_LINE
+ case value_t::number_integer: // LCOV_EXCL_LINE
+ case value_t::number_unsigned: // LCOV_EXCL_LINE
+ case value_t::number_float: // LCOV_EXCL_LINE
+ case value_t::binary: // LCOV_EXCL_LINE
+ case value_t::discarded: // LCOV_EXCL_LINE
+ default: // LCOV_EXCL_LINE
+ JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
+ }
+ };
+
+ // wrapper for "remove" operation; remove value at ptr
+ const auto operation_remove = [this, & result](json_pointer & ptr)
+ {
+ // get reference to parent of JSON pointer ptr
+ const auto last_path = ptr.back();
+ ptr.pop_back();
+ basic_json& parent = result.at(ptr);
+
+ // remove child
+ if (parent.is_object())
+ {
+ // perform range check
+ auto it = parent.find(last_path);
+ if (JSON_HEDLEY_LIKELY(it != parent.end()))
+ {
+ parent.erase(it);
+ }
+ else
+ {
+ JSON_THROW(out_of_range::create(403, detail::concat("key '", last_path, "' not found"), this));
+ }
+ }
+ else if (parent.is_array())
+ {
+ // note erase performs range check
+ parent.erase(json_pointer::template array_index<basic_json_t>(last_path));
+ }
+ };
+
+ // type check: top level value must be an array
+ if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
+ {
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &json_patch));
+ }
+
+ // iterate and apply the operations
+ for (const auto& val : json_patch)
+ {
+ // wrapper to get a value for an operation
+ const auto get_value = [&val](const string_t& op,
+ const string_t& member,
+ bool string_type) -> basic_json &
+ {
+ // find value
+ auto it = val.m_data.m_value.object->find(member);
+
+ // context-sensitive error message
+ const auto error_msg = (op == "op") ? "operation" : detail::concat("operation '", op, '\''); // NOLINT(bugprone-unused-local-non-trivial-variable)
+
+ // check if desired value is present
+ if (JSON_HEDLEY_UNLIKELY(it == val.m_data.m_value.object->end()))
+ {
+ // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+ JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have member '", member, "'"), &val));
+ }
+
+ // check if result is of type string
+ if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
+ {
+ // NOLINTNEXTLINE(performance-inefficient-string-concatenation)
+ JSON_THROW(parse_error::create(105, 0, detail::concat(error_msg, " must have string member '", member, "'"), &val));
+ }
+
+ // no error: return value
+ return it->second;
+ };
+
+ // type check: every element of the array must be an object
+ if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
+ {
+ JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", &val));
+ }
+
+ // collect mandatory members
+ const auto op = get_value("op", "op", true).template get<string_t>();
+ const auto path = get_value(op, "path", true).template get<string_t>();
+ json_pointer ptr(path);
+
+ switch (get_op(op))
+ {
+ case patch_operations::add:
+ {
+ operation_add(ptr, get_value("add", "value", false));
+ break;
+ }
+
+ case patch_operations::remove:
+ {
+ operation_remove(ptr);
+ break;
+ }
+
+ case patch_operations::replace:
+ {
+ // the "path" location must exist - use at()
+ result.at(ptr) = get_value("replace", "value", false);
+ break;
+ }
+
+ case patch_operations::move:
+ {
+ const auto from_path = get_value("move", "from", true).template get<string_t>();
+ json_pointer from_ptr(from_path);
+
+ // the "from" location must exist - use at()
+ basic_json const v = result.at(from_ptr);
+
+ // The move operation is functionally identical to a
+ // "remove" operation on the "from" location, followed
+ // immediately by an "add" operation at the target
+ // location with the value that was just removed.
+ operation_remove(from_ptr);
+ operation_add(ptr, v);
+ break;
+ }
+
+ case patch_operations::copy:
+ {
+ const auto from_path = get_value("copy", "from", true).template get<string_t>();
+ const json_pointer from_ptr(from_path);
+
+ // the "from" location must exist - use at()
+ basic_json const v = result.at(from_ptr);
+
+ // The copy is functionally identical to an "add"
+ // operation at the target location using the value
+ // specified in the "from" member.
+ operation_add(ptr, v);
+ break;
+ }
+
+ case patch_operations::test:
+ {
+ bool success = false;
+ JSON_TRY
+ {
+ // check if "value" matches the one at "path"
+ // the "path" location must exist - use at()
+ success = (result.at(ptr) == get_value("test", "value", false));
+ }
+ JSON_INTERNAL_CATCH (out_of_range&)
+ {
+ // ignore out of range errors: success remains false
+ }
+
+ // throw an exception if test fails
+ if (JSON_HEDLEY_UNLIKELY(!success))
+ {
+ JSON_THROW(other_error::create(501, detail::concat("unsuccessful: ", val.dump()), &val));
+ }
+
+ break;
+ }
+
+ case patch_operations::invalid:
+ default:
+ {
+ // op must be "add", "remove", "replace", "move", "copy", or
+ // "test"
+ JSON_THROW(parse_error::create(105, 0, detail::concat("operation value '", op, "' is invalid"), &val));
+ }
+ }
+ }
+ }
+
+ /// @brief applies a JSON patch to a copy of the current object
+ /// @sa https://json.nlohmann.me/api/basic_json/patch/
+ basic_json patch(const basic_json& json_patch) const
+ {
+ basic_json result = *this;
+ result.patch_inplace(json_patch);
+ return result;
+ }
+
+ /// @brief creates a diff as a JSON patch
+ /// @sa https://json.nlohmann.me/api/basic_json/diff/
+ JSON_HEDLEY_WARN_UNUSED_RESULT
+ static basic_json diff(const basic_json& source, const basic_json& target,
+ const string_t& path = "")
+ {
+ // the patch
+ basic_json result(value_t::array);
+
+ // if the values are the same, return empty patch
+ if (source == target)
+ {
+ return result;
+ }
+
+ if (source.type() != target.type())
+ {
+ // different types: replace value
+ result.push_back(
+ {
+ {"op", "replace"}, {"path", path}, {"value", target}
+ });
+ return result;
+ }
+
+ switch (source.type())
+ {
+ case value_t::array:
+ {
+ // first pass: traverse common elements
+ std::size_t i = 0;
+ while (i < source.size() && i < target.size())
+ {
+ // recursive call to compare array values at index i
+ auto temp_diff = diff(source[i], target[i], detail::concat<string_t>(path, '/', detail::to_string<string_t>(i)));
+ result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+ ++i;
+ }
+
+ // We now reached the end of at least one array
+ // in a second pass, traverse the remaining elements
+
+ // remove my remaining elements
+ const auto end_index = static_cast<difference_type>(result.size());
+ while (i < source.size())
+ {
+ // add operations in reverse order to avoid invalid
+ // indices
+ result.insert(result.begin() + end_index, object(
+ {
+ {"op", "remove"},
+ {"path", detail::concat<string_t>(path, '/', detail::to_string<string_t>(i))}
+ }));
+ ++i;
+ }
+
+ // add other remaining elements
+ while (i < target.size())
+ {
+ result.push_back(
+ {
+ {"op", "add"},
+ {"path", detail::concat<string_t>(path, "/-")},
+ {"value", target[i]}
+ });
+ ++i;
+ }
+
+ break;
+ }
+
+ case value_t::object:
+ {
+ // first pass: traverse this object's elements
+ for (auto it = source.cbegin(); it != source.cend(); ++it)
+ {
+ // escape the key name to be used in a JSON patch
+ const auto path_key = detail::concat<string_t>(path, '/', detail::escape(it.key()));
+
+ if (target.find(it.key()) != target.end())
+ {
+ // recursive call to compare object values at key it
+ auto temp_diff = diff(it.value(), target[it.key()], path_key);
+ result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+ }
+ else
+ {
+ // found a key that is not in o -> remove it
+ result.push_back(object(
+ {
+ {"op", "remove"}, {"path", path_key}
+ }));
+ }
+ }
+
+ // second pass: traverse other object's elements
+ for (auto it = target.cbegin(); it != target.cend(); ++it)
+ {
+ if (source.find(it.key()) == source.end())
+ {
+ // found a key that is not in this -> add it
+ const auto path_key = detail::concat<string_t>(path, '/', detail::escape(it.key()));
+ result.push_back(
+ {
+ {"op", "add"}, {"path", path_key},
+ {"value", it.value()}
+ });
+ }
+ }
+
+ break;
+ }
+
+ case value_t::null:
+ case value_t::string:
+ case value_t::boolean:
+ case value_t::number_integer:
+ case value_t::number_unsigned:
+ case value_t::number_float:
+ case value_t::binary:
+ case value_t::discarded:
+ default:
+ {
+ // both primitive type: replace value
+ result.push_back(
+ {
+ {"op", "replace"}, {"path", path}, {"value", target}
+ });
+ break;
+ }
+ }
+
+ return result;
+ }
+ /// @}
+
+ ////////////////////////////////
+ // JSON Merge Patch functions //
+ ////////////////////////////////
+
+ /// @name JSON Merge Patch functions
+ /// @{
+
+ /// @brief applies a JSON Merge Patch
+ /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/
+ void merge_patch(const basic_json& apply_patch)
+ {
+ if (apply_patch.is_object())
+ {
+ if (!is_object())
+ {
+ *this = object();
+ }
+ for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)
+ {
+ if (it.value().is_null())
+ {
+ erase(it.key());
+ }
+ else
+ {
+ operator[](it.key()).merge_patch(it.value());
+ }
+ }
+ }
+ else
+ {
+ *this = apply_patch;
+ }
+ }
+
+ /// @}
+};
+
+/// @brief user-defined to_string function for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/to_string/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j)
+{
+ return j.dump();
+}
+
+inline namespace literals
+{
+inline namespace json_literals
+{
+
+/// @brief user-defined string literal for JSON values
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/
+JSON_HEDLEY_NON_NULL(1)
+#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
+ inline nlohmann::json operator ""_json(const char* s, std::size_t n)
+#else
+ inline nlohmann::json operator "" _json(const char* s, std::size_t n)
+#endif
+{
+ return nlohmann::json::parse(s, s + n);
+}
+
+/// @brief user-defined string literal for JSON pointer
+/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/
+JSON_HEDLEY_NON_NULL(1)
+#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
+ inline nlohmann::json::json_pointer operator ""_json_pointer(const char* s, std::size_t n)
+#else
+ inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n)
+#endif
+{
+ return nlohmann::json::json_pointer(std::string(s, n));
+}
+
+} // namespace json_literals
+} // namespace literals
+NLOHMANN_JSON_NAMESPACE_END
+
+///////////////////////
+// nonmember support //
+///////////////////////
+
+namespace std // NOLINT(cert-dcl58-cpp)
+{
+
+/// @brief hash value for JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_hash/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+struct hash<nlohmann::NLOHMANN_BASIC_JSON_TPL> // NOLINT(cert-dcl58-cpp)
+{
+ std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const
+ {
+ return nlohmann::detail::hash(j);
+ }
+};
+
+// specialization for std::less<value_t>
+template<>
+struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679
+{
+ /*!
+ @brief compare two value_t enum values
+ @since version 3.0.0
+ */
+ bool operator()(::nlohmann::detail::value_t lhs,
+ ::nlohmann::detail::value_t rhs) const noexcept
+ {
+#if JSON_HAS_THREE_WAY_COMPARISON
+ return std::is_lt(lhs <=> rhs); // *NOPAD*
+#else
+ return ::nlohmann::detail::operator<(lhs, rhs);
+#endif
+ }
+};
+
+// C++20 prohibit function specialization in the std namespace.
+#ifndef JSON_HAS_CPP_20
+
+/// @brief exchanges the values of two JSON objects
+/// @sa https://json.nlohmann.me/api/basic_json/std_swap/
+NLOHMANN_BASIC_JSON_TPL_DECLARATION
+inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name, cert-dcl58-cpp)
+ is_nothrow_move_constructible<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value&& // NOLINT(misc-redundant-expression,cppcoreguidelines-noexcept-swap,performance-noexcept-swap)
+ is_nothrow_move_assignable<nlohmann::NLOHMANN_BASIC_JSON_TPL>::value)
+{
+ j1.swap(j2);
+}
+
+#endif
+
+} // namespace std
+
+#if JSON_USE_GLOBAL_UDLS
+ #if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
+ using nlohmann::literals::json_literals::operator ""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+ using nlohmann::literals::json_literals::operator ""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+ #else
+ using nlohmann::literals::json_literals::operator "" _json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+ using nlohmann::literals::json_literals::operator "" _json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+ #endif
+#endif
+
+// #include <nlohmann/detail/macro_unscope.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+// restore clang diagnostic settings
+#if defined(__clang__)
+ #pragma clang diagnostic pop
+#endif
+
+// clean up
+#undef JSON_ASSERT
+#undef JSON_INTERNAL_CATCH
+#undef JSON_THROW
+#undef JSON_PRIVATE_UNLESS_TESTED
+#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION
+#undef NLOHMANN_BASIC_JSON_TPL
+#undef JSON_EXPLICIT
+#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL
+#undef JSON_INLINE_VARIABLE
+#undef JSON_NO_UNIQUE_ADDRESS
+#undef JSON_DISABLE_ENUM_SERIALIZATION
+#undef JSON_USE_GLOBAL_UDLS
+
+#ifndef JSON_TEST_KEEP_MACROS
+ #undef JSON_CATCH
+ #undef JSON_TRY
+ #undef JSON_HAS_CPP_11
+ #undef JSON_HAS_CPP_14
+ #undef JSON_HAS_CPP_17
+ #undef JSON_HAS_CPP_20
+ #undef JSON_HAS_CPP_23
+ #undef JSON_HAS_FILESYSTEM
+ #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM
+ #undef JSON_HAS_THREE_WAY_COMPARISON
+ #undef JSON_HAS_RANGES
+ #undef JSON_HAS_STATIC_RTTI
+ #undef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
+#endif
+
+// #include <nlohmann/thirdparty/hedley/hedley_undef.hpp>
+// __ _____ _____ _____
+// __| | __| | | | JSON for Modern C++
+// | | |__ | | | | | | version 3.12.0
+// |_____|_____|_____|_|___| https://github.com/nlohmann/json
+//
+// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
+// SPDX-License-Identifier: MIT
+
+
+
+#undef JSON_HEDLEY_ALWAYS_INLINE
+#undef JSON_HEDLEY_ARM_VERSION
+#undef JSON_HEDLEY_ARM_VERSION_CHECK
+#undef JSON_HEDLEY_ARRAY_PARAM
+#undef JSON_HEDLEY_ASSUME
+#undef JSON_HEDLEY_BEGIN_C_DECLS
+#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_BUILTIN
+#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_CLANG_HAS_EXTENSION
+#undef JSON_HEDLEY_CLANG_HAS_FEATURE
+#undef JSON_HEDLEY_CLANG_HAS_WARNING
+#undef JSON_HEDLEY_COMPCERT_VERSION
+#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK
+#undef JSON_HEDLEY_CONCAT
+#undef JSON_HEDLEY_CONCAT3
+#undef JSON_HEDLEY_CONCAT3_EX
+#undef JSON_HEDLEY_CONCAT_EX
+#undef JSON_HEDLEY_CONST
+#undef JSON_HEDLEY_CONSTEXPR
+#undef JSON_HEDLEY_CONST_CAST
+#undef JSON_HEDLEY_CPP_CAST
+#undef JSON_HEDLEY_CRAY_VERSION
+#undef JSON_HEDLEY_CRAY_VERSION_CHECK
+#undef JSON_HEDLEY_C_DECL
+#undef JSON_HEDLEY_DEPRECATED
+#undef JSON_HEDLEY_DEPRECATED_FOR
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
+#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION
+#undef JSON_HEDLEY_DIAGNOSTIC_POP
+#undef JSON_HEDLEY_DIAGNOSTIC_PUSH
+#undef JSON_HEDLEY_DMC_VERSION
+#undef JSON_HEDLEY_DMC_VERSION_CHECK
+#undef JSON_HEDLEY_EMPTY_BASES
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION
+#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK
+#undef JSON_HEDLEY_END_C_DECLS
+#undef JSON_HEDLEY_FLAGS
+#undef JSON_HEDLEY_FLAGS_CAST
+#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_BUILTIN
+#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GCC_HAS_EXTENSION
+#undef JSON_HEDLEY_GCC_HAS_FEATURE
+#undef JSON_HEDLEY_GCC_HAS_WARNING
+#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
+#undef JSON_HEDLEY_GCC_VERSION
+#undef JSON_HEDLEY_GCC_VERSION_CHECK
+#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_BUILTIN
+#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_GNUC_HAS_EXTENSION
+#undef JSON_HEDLEY_GNUC_HAS_FEATURE
+#undef JSON_HEDLEY_GNUC_HAS_WARNING
+#undef JSON_HEDLEY_GNUC_VERSION
+#undef JSON_HEDLEY_GNUC_VERSION_CHECK
+#undef JSON_HEDLEY_HAS_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_BUILTIN
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS
+#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE
+#undef JSON_HEDLEY_HAS_EXTENSION
+#undef JSON_HEDLEY_HAS_FEATURE
+#undef JSON_HEDLEY_HAS_WARNING
+#undef JSON_HEDLEY_IAR_VERSION
+#undef JSON_HEDLEY_IAR_VERSION_CHECK
+#undef JSON_HEDLEY_IBM_VERSION
+#undef JSON_HEDLEY_IBM_VERSION_CHECK
+#undef JSON_HEDLEY_IMPORT
+#undef JSON_HEDLEY_INLINE
+#undef JSON_HEDLEY_INTEL_CL_VERSION
+#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK
+#undef JSON_HEDLEY_INTEL_VERSION
+#undef JSON_HEDLEY_INTEL_VERSION_CHECK
+#undef JSON_HEDLEY_IS_CONSTANT
+#undef JSON_HEDLEY_IS_CONSTEXPR_
+#undef JSON_HEDLEY_LIKELY
+#undef JSON_HEDLEY_MALLOC
+#undef JSON_HEDLEY_MCST_LCC_VERSION
+#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK
+#undef JSON_HEDLEY_MESSAGE
+#undef JSON_HEDLEY_MSVC_VERSION
+#undef JSON_HEDLEY_MSVC_VERSION_CHECK
+#undef JSON_HEDLEY_NEVER_INLINE
+#undef JSON_HEDLEY_NON_NULL
+#undef JSON_HEDLEY_NO_ESCAPE
+#undef JSON_HEDLEY_NO_RETURN
+#undef JSON_HEDLEY_NO_THROW
+#undef JSON_HEDLEY_NULL
+#undef JSON_HEDLEY_PELLES_VERSION
+#undef JSON_HEDLEY_PELLES_VERSION_CHECK
+#undef JSON_HEDLEY_PGI_VERSION
+#undef JSON_HEDLEY_PGI_VERSION_CHECK
+#undef JSON_HEDLEY_PREDICT
+#undef JSON_HEDLEY_PRINTF_FORMAT
+#undef JSON_HEDLEY_PRIVATE
+#undef JSON_HEDLEY_PUBLIC
+#undef JSON_HEDLEY_PURE
+#undef JSON_HEDLEY_REINTERPRET_CAST
+#undef JSON_HEDLEY_REQUIRE
+#undef JSON_HEDLEY_REQUIRE_CONSTEXPR
+#undef JSON_HEDLEY_REQUIRE_MSG
+#undef JSON_HEDLEY_RESTRICT
+#undef JSON_HEDLEY_RETURNS_NON_NULL
+#undef JSON_HEDLEY_SENTINEL
+#undef JSON_HEDLEY_STATIC_ASSERT
+#undef JSON_HEDLEY_STATIC_CAST
+#undef JSON_HEDLEY_STRINGIFY
+#undef JSON_HEDLEY_STRINGIFY_EX
+#undef JSON_HEDLEY_SUNPRO_VERSION
+#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK
+#undef JSON_HEDLEY_TINYC_VERSION
+#undef JSON_HEDLEY_TINYC_VERSION_CHECK
+#undef JSON_HEDLEY_TI_ARMCL_VERSION
+#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL2000_VERSION
+#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL430_VERSION
+#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL6X_VERSION
+#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CL7X_VERSION
+#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK
+#undef JSON_HEDLEY_TI_CLPRU_VERSION
+#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK
+#undef JSON_HEDLEY_TI_VERSION
+#undef JSON_HEDLEY_TI_VERSION_CHECK
+#undef JSON_HEDLEY_UNAVAILABLE
+#undef JSON_HEDLEY_UNLIKELY
+#undef JSON_HEDLEY_UNPREDICTABLE
+#undef JSON_HEDLEY_UNREACHABLE
+#undef JSON_HEDLEY_UNREACHABLE_RETURN
+#undef JSON_HEDLEY_VERSION
+#undef JSON_HEDLEY_VERSION_DECODE_MAJOR
+#undef JSON_HEDLEY_VERSION_DECODE_MINOR
+#undef JSON_HEDLEY_VERSION_DECODE_REVISION
+#undef JSON_HEDLEY_VERSION_ENCODE
+#undef JSON_HEDLEY_WARNING
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT
+#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG
+#undef JSON_HEDLEY_FALL_THROUGH
+
+
+
+#endif // INCLUDE_NLOHMANN_JSON_HPP_
diff --git a/Minecraft.World/CompressedTileStorage.cpp b/Minecraft.World/CompressedTileStorage.cpp
index 1c387eeb..b093f804 100644
--- a/Minecraft.World/CompressedTileStorage.cpp
+++ b/Minecraft.World/CompressedTileStorage.cpp
@@ -1311,7 +1311,7 @@ int CompressedTileStorage::getHighestNonEmptyY()
// Multiply by the number of vertical tiles in a block, and then add that again to be at the top of the block
highestNonEmptyY = (highestYBlock * 4) + 4;
}
- else
+ else if( allocatedSize != 1024 )
{
app.DebugPrintf("[CTS-WARN] getHighestNonEmptyY() returned -1! allocatedSize=%d indicesAndData=%p\n",
allocatedSize, indicesAndData);
@@ -1380,4 +1380,4 @@ void CompressedTileStorage::reverseIndices(unsigned char *indices)
{
System::ReverseUSHORT(&blockIndices[i]);
}
-} \ No newline at end of file
+}
diff --git a/Minecraft.World/MapItemSavedData.cpp b/Minecraft.World/MapItemSavedData.cpp
index 0a5eb7c4..358b9c7e 100644
--- a/Minecraft.World/MapItemSavedData.cpp
+++ b/Minecraft.World/MapItemSavedData.cpp
@@ -12,6 +12,28 @@
const int MapItemSavedData::END_PORTAL_DECORATION_KEY = -1;
+namespace
+{
+ // Valid player icon slots in mapicons.png/additionalmapicons.png.
+
+ // Keep to these slots so we never sample placeholder entries.
+ static const char PLAYER_MAP_ICON_SLOTS[] = { 0, 1, 2, 3, 8, 9, 10, 11 };
+
+ static char getRandomPlayerMapIcon(const shared_ptr<Player>& player)
+ {
+ // use seed bit shift random
+ unsigned int seed = static_cast<unsigned int>(player->entityId);
+ seed ^= static_cast<unsigned int>(player->getPlayerIndex() * 0x9E3779B9u);
+ seed ^= (seed >> 16);
+ seed *= 0x7FEB352Du;
+ seed ^= (seed >> 15);
+ seed *= 0x846CA68Bu;
+ seed ^= (seed >> 16);
+
+ return PLAYER_MAP_ICON_SLOTS[seed % (sizeof(PLAYER_MAP_ICON_SLOTS) / sizeof(PLAYER_MAP_ICON_SLOTS[0]))];
+ }
+}
+
// 4J added entityId param
MapItemSavedData::MapDecoration::MapDecoration(char img, char x, char y, char rot, int entityId, bool visible)
{
@@ -423,16 +445,14 @@ void MapItemSavedData::tickCarriedBy(shared_ptr<Player> player, shared_ptr<ItemI
// 4J Stu - As we have added new icons for players on a new row below
// other icons used in Java we need to move our index to the next row
- imgIndex = static_cast<int>(decorationPlayer->getPlayerIndex());
- if(imgIndex>3) imgIndex += 4;
+ imgIndex = getRandomPlayerMapIcon(decorationPlayer);
}
#ifdef _LARGE_WORLDS
else //if (abs(xd) < MAP_SIZE * 5 && abs(yd) < MAP_SIZE * 5)
{
// 4J Stu - As we have added new icons for players on a new row below
// other icons used in Java we need to move our index to the next row
- imgIndex = static_cast<int>(decorationPlayer->getPlayerIndex());
- if(imgIndex>3) imgIndex += 4;
+ imgIndex = getRandomPlayerMapIcon(decorationPlayer);
imgIndex += 16; // Add 16 to indicate that it's on the next texture
rot = 0;
diff --git a/MinecraftConsoles.sln b/MinecraftConsoles.sln
index 1ce39567..9eb80a4b 100644
--- a/MinecraftConsoles.sln
+++ b/MinecraftConsoles.sln
@@ -1,4 +1,4 @@
-
+
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.37012.4 d17.14
@@ -10,6 +10,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Minecraft.Client", "Minecra
{F046C3CE-9749-4823-B32B-D9CC10B1A2C8} = {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}
EndProjectSection
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Minecraft.Server", "Minecraft.Server\Minecraft.Server.vcxproj", "{7CB40BFC-C8E4-4293-A22E-D2041348D5AF}"
+ ProjectSection(ProjectDependencies) = postProject
+ {F046C3CE-9749-4823-B32B-D9CC10B1A2C8} = {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
ContentPackage_NO_TU|ARM64EC = ContentPackage_NO_TU|ARM64EC
@@ -223,6 +228,54 @@ Global
{1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Windows64.ActiveCfg = ReleaseForArt|x64
{1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Xbox 360.ActiveCfg = Release|Xbox 360
{1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Xbox 360.Build.0 = Release|Xbox 360
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage_NO_TU|ARM64EC.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage_NO_TU|Durango.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage_NO_TU|ORBIS.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage_NO_TU|PS3.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage_NO_TU|PSVita.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage_NO_TU|Windows64.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage_NO_TU|Windows64.Build.0 = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage_NO_TU|Xbox 360.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.CONTENTPACKAGE_SYMBOLS|ARM64EC.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.CONTENTPACKAGE_SYMBOLS|Durango.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.CONTENTPACKAGE_SYMBOLS|ORBIS.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.CONTENTPACKAGE_SYMBOLS|PS3.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.CONTENTPACKAGE_SYMBOLS|PSVita.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.CONTENTPACKAGE_SYMBOLS|Windows64.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.CONTENTPACKAGE_SYMBOLS|Windows64.Build.0 = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.CONTENTPACKAGE_SYMBOLS|Xbox 360.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage|ARM64EC.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage|Durango.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage|ORBIS.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage|PS3.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage|PSVita.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage|Windows64.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage|Windows64.Build.0 = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ContentPackage|Xbox 360.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Debug|ARM64EC.ActiveCfg = Debug|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Debug|Durango.ActiveCfg = Debug|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Debug|ORBIS.ActiveCfg = Debug|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Debug|PS3.ActiveCfg = Debug|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Debug|PSVita.ActiveCfg = Debug|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Debug|Windows64.ActiveCfg = Debug|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Debug|Windows64.Build.0 = Debug|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Debug|Xbox 360.ActiveCfg = Debug|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Release|ARM64EC.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Release|Durango.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Release|ORBIS.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Release|PS3.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Release|PSVita.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Release|Windows64.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Release|Windows64.Build.0 = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.Release|Xbox 360.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ReleaseForArt|ARM64EC.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ReleaseForArt|Durango.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ReleaseForArt|ORBIS.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ReleaseForArt|PS3.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ReleaseForArt|PSVita.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ReleaseForArt|Windows64.ActiveCfg = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ReleaseForArt|Windows64.Build.0 = Release|x64
+ {7CB40BFC-C8E4-4293-A22E-D2041348D5AF}.ReleaseForArt|Xbox 360.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/README.md b/README.md
index 0c476666..81fa190d 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,114 @@ Example:
Minecraft.Client.exe -name Steve -fullscreen
```
+## Dedicated Server in Docker (Wine)
+
+This repository includes a lightweight Docker setup for running the Windows dedicated server under Wine.
+### Quick Start (No Build, Recommended)
+
+No local build is required. The container image is pulled from GHCR.
+
+```bash
+./start-dedicated-server.sh
+```
+
+`start-dedicated-server.sh` does the following:
+- uses `docker-compose.dedicated-server.ghcr.yml`
+- pulls latest image, then starts the container
+
+If you want to skip pulling and just start:
+
+```bash
+./start-dedicated-server.sh --no-pull
+```
+
+Equivalent manual command:
+
+```bash
+docker compose -f docker-compose.dedicated-server.ghcr.yml up -d
+```
+
+### Local Build Mode (Optional)
+
+Use this only when you want to run your own locally built `Minecraft.Server` binary in Docker.
+**A local build of `Minecraft.Server` is required for this mode.**
+
+```bash
+docker compose -f docker-compose.dedicated-server.yml up -d --build
+```
+
+Useful environment variables:
+- `XVFB_DISPLAY` (default: `:99`)
+- `XVFB_SCREEN` (default: `64x64x16`, tiny virtual display used by Wine)
+
+Fixed server runtime behavior in container:
+- executable path: `/srv/mc/Minecraft.Server.exe`
+- bind IP: `0.0.0.0`
+- server port: `25565`
+
+Persistent files are bind-mounted to host:
+- `./server-data/server.properties` -> `/srv/mc/server.properties`
+- `./server-data/GameHDD` -> `/srv/mc/Windows64/GameHDD`
+
+### About `server.properties`
+
+`Minecraft.Server` reads `server.properties` from the executable working directory (Docker image: `/srv/mc/server.properties`).
+If the file is missing or contains invalid values, defaults are auto-generated/normalized on startup.
+
+Important keys:
+
+| Key | Values / Range | Default | Notes |
+|-----|-----------------|---------|-------|
+| `server-port` | `1-65535` | `25565` | Listen TCP port |
+| `server-ip` | string | `0.0.0.0` | Bind address |
+| `server-name` | string (max 16 chars) | `DedicatedServer` | Host display name |
+| `max-players` | `1-8` | `8` | Public player slots |
+| `level-name` | string | `world` | Display world name |
+| `level-id` | safe ID string | derived from `level-name` | Save folder ID; normalized automatically |
+| `level-seed` | int64 or empty | empty | Empty = random seed |
+| `world-size` | `classic\|small\|medium\|large` | `classic` | World size preset for new worlds and expansion target for existing worlds |
+| `log-level` | `debug\|info\|warn\|error` | `info` | Server log verbosity |
+| `autosave-interval` | `5-3600` | `60` | Seconds between autosaves |
+| `white-list` | `true/false` | `false` | Enable access list checks |
+| `lan-advertise` | `true/false` | `false` | LAN session advertisement |
+
+Minimal example:
+
+```properties
+server-name=DedicatedServer
+server-port=25565
+max-players=8
+level-name=world
+level-seed=
+world-size=classic
+log-level=info
+white-list=false
+lan-advertise=false
+autosave-interval=60
+```
+
+### Dedicated Server launch arguments
+
+The server loads base settings from `server.properties`, then CLI arguments override those values.
+
+| Argument | Description |
+|----------|-------------|
+| `-port <1-65535>` | Override `server-port` |
+| `-ip <addr>` | Override `server-ip` |
+| `-bind <addr>` | Alias of `-ip` |
+| `-name <name>` | Override `server-name` (max 16 chars) |
+| `-maxplayers <1-8>` | Override `max-players` |
+| `-seed <int64>` | Override `level-seed` |
+| `-loglevel <level>` | Override `log-level` (`debug`, `info`, `warn`, `error`) |
+| `-help` / `--help` / `-h` | Print usage and exit |
+
+Examples:
+
+```powershell
+Minecraft.Server.exe -name MyServer -port 25565 -ip 0.0.0.0 -maxplayers 8 -loglevel info
+Minecraft.Server.exe -seed 123456789
+```
+
## Controls (Keyboard & Mouse)
- **Movement**: `W` `A` `S` `D`
diff --git a/build-start-dedicated-server.sh b/build-start-dedicated-server.sh
new file mode 100644
index 00000000..7f6bd3a7
--- /dev/null
+++ b/build-start-dedicated-server.sh
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
+COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.dedicated-server.yml"
+SERVICE_NAME="minecraft-lce-dedicated-server"
+PERSIST_DIR="${SCRIPT_DIR}/server-data"
+
+if [[ ! -f "${COMPOSE_FILE}" ]]; then
+ echo "[error] docker-compose file not found: ${COMPOSE_FILE}" >&2
+ exit 1
+fi
+
+if command -v docker >/dev/null 2>&1; then
+ COMPOSE_CMD=(docker compose)
+elif command -v docker-compose >/dev/null 2>&1; then
+ COMPOSE_CMD=(docker-compose)
+else
+ echo "[error] docker compose is not available." >&2
+ exit 1
+fi
+
+# can choose between release and debug, but honestly there isn’t much use
+if [[ "${#}" -gt 1 ]]; then
+ echo "Usage: $0 [release|debug|<runtime-dir-in-repo>]" >&2
+ exit 1
+fi
+
+if [[ "${#}" -eq 1 ]]; then
+ case "${1}" in
+ release)
+ MC_RUNTIME_DIR_INPUT="x64/Minecraft.Server/Release"
+ ;;
+ debug)
+ MC_RUNTIME_DIR_INPUT="x64/Minecraft.Server/Debug"
+ ;;
+ *)
+ MC_RUNTIME_DIR_INPUT="${1}"
+ ;;
+ esac
+else
+ if [[ -f "${SCRIPT_DIR}/x64/Minecraft.Server/Release/Minecraft.Server.exe" ]]; then
+ MC_RUNTIME_DIR_INPUT="x64/Minecraft.Server/Release"
+ else
+ MC_RUNTIME_DIR_INPUT="x64/Minecraft.Server/Debug"
+ fi
+fi
+
+if [[ "${MC_RUNTIME_DIR_INPUT}" = /* ]]; then
+ if [[ "${MC_RUNTIME_DIR_INPUT}" != "${SCRIPT_DIR}"/* ]]; then
+ echo "[error] runtime-dir must be inside repository: ${MC_RUNTIME_DIR_INPUT}" >&2
+ exit 1
+ fi
+ MC_RUNTIME_DIR_REL="${MC_RUNTIME_DIR_INPUT#${SCRIPT_DIR}/}"
+else
+ MC_RUNTIME_DIR_REL="${MC_RUNTIME_DIR_INPUT#./}"
+fi
+
+if [[ -z "${MC_RUNTIME_DIR_REL}" || "${MC_RUNTIME_DIR_REL}" = "." ]]; then
+ echo "[error] runtime-dir is invalid: ${MC_RUNTIME_DIR_INPUT}" >&2
+ exit 1
+fi
+
+if ! RUNTIME_ABS="$(cd "${SCRIPT_DIR}" && cd "${MC_RUNTIME_DIR_REL}" 2>/dev/null && pwd)"; then
+ echo "[error] runtime-dir not found: ${MC_RUNTIME_DIR_INPUT}" >&2
+ exit 1
+fi
+
+if [[ "${RUNTIME_ABS}" != "${SCRIPT_DIR}"/* ]]; then
+ echo "[error] runtime-dir must resolve inside repository: ${MC_RUNTIME_DIR_INPUT}" >&2
+ exit 1
+fi
+
+MC_RUNTIME_DIR_REL="${RUNTIME_ABS#${SCRIPT_DIR}/}"
+
+if [[ ! -f "${RUNTIME_ABS}/Minecraft.Server.exe" ]]; then
+ echo "[error] Minecraft.Server.exe not found in: ${RUNTIME_ABS}" >&2
+ echo "[hint] Build dedicated server first, then retry." >&2
+ exit 1
+fi
+
+echo "[info] Using runtime (build arg): ${MC_RUNTIME_DIR_REL}"
+echo "[info] Persistent data dir: ${PERSIST_DIR}"
+MC_RUNTIME_DIR="${MC_RUNTIME_DIR_REL}" "${COMPOSE_CMD[@]}" -f "${COMPOSE_FILE}" up -d --build "${SERVICE_NAME}"
+echo "[info] Dedicated server started."
diff --git a/cmake/CopyServerAssets.cmake b/cmake/CopyServerAssets.cmake
new file mode 100644
index 00000000..d1e6fa4e
--- /dev/null
+++ b/cmake/CopyServerAssets.cmake
@@ -0,0 +1,72 @@
+if(NOT DEFINED PROJECT_SOURCE_DIR OR NOT DEFINED OUTPUT_DIR OR NOT DEFINED CONFIGURATION)
+ message(FATAL_ERROR "CopyServerAssets.cmake requires PROJECT_SOURCE_DIR, OUTPUT_DIR, and CONFIGURATION.")
+endif()
+
+string(REPLACE "\"" "" PROJECT_SOURCE_DIR "${PROJECT_SOURCE_DIR}")
+string(REPLACE "\"" "" OUTPUT_DIR "${OUTPUT_DIR}")
+string(REPLACE "\"" "" CONFIGURATION "${CONFIGURATION}")
+
+set(_project_dir "${PROJECT_SOURCE_DIR}/Minecraft.Client")
+
+function(copy_tree_if_exists src_rel dst_rel)
+ set(_src "${_project_dir}/${src_rel}")
+ set(_dst "${OUTPUT_DIR}/${dst_rel}")
+
+ if(EXISTS "${_src}")
+ file(MAKE_DIRECTORY "${_dst}")
+ file(GLOB_RECURSE _files RELATIVE "${_src}" "${_src}/*")
+
+ foreach(_file IN LISTS _files)
+ if(NOT _file MATCHES "\\.(cpp|c|h|hpp|xml|lang)$")
+ set(_full_src "${_src}/${_file}")
+ set(_full_dst "${_dst}/${_file}")
+
+ if(IS_DIRECTORY "${_full_src}")
+ file(MAKE_DIRECTORY "${_full_dst}")
+ else()
+ get_filename_component(_dst_dir "${_full_dst}" DIRECTORY)
+ file(MAKE_DIRECTORY "${_dst_dir}")
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different
+ "${_full_src}" "${_full_dst}"
+ )
+ endif()
+ endif()
+ endforeach()
+ endif()
+endfunction()
+
+function(copy_file_if_exists src_rel dst_rel)
+ set(_src "${PROJECT_SOURCE_DIR}/${src_rel}")
+ set(_dst "${OUTPUT_DIR}/${dst_rel}")
+
+ get_filename_component(_dst_dir "${_dst}" DIRECTORY)
+ file(MAKE_DIRECTORY "${_dst_dir}")
+
+ if(EXISTS "${_src}")
+ execute_process(
+ COMMAND "${CMAKE_COMMAND}" -E copy_if_different
+ "${_src}" "${_dst}"
+ )
+ endif()
+endfunction()
+
+function(copy_first_existing dst_rel)
+ foreach(_candidate IN LISTS ARGN)
+ if(EXISTS "${PROJECT_SOURCE_DIR}/${_candidate}")
+ copy_file_if_exists("${_candidate}" "${dst_rel}")
+ return()
+ endif()
+ endforeach()
+endfunction()
+
+# Dedicated server runtime assets (minimal set validated in startup tests).
+copy_file_if_exists("Minecraft.Client/Common/Media/MediaWindows64.arc" "Common/Media/MediaWindows64.arc")
+copy_tree_if_exists("Common/res" "Common/res")
+copy_tree_if_exists("Windows64/GameHDD" "Windows64/GameHDD")
+
+copy_first_existing("iggy_w64.dll"
+ "Minecraft.Client/Windows64/Iggy/lib/redist64/iggy_w64.dll"
+ "x64/${CONFIGURATION}/iggy_w64.dll"
+)
+
diff --git a/docker-build-dedicated-server.sh b/docker-build-dedicated-server.sh
new file mode 100644
index 00000000..d84f7c61
--- /dev/null
+++ b/docker-build-dedicated-server.sh
@@ -0,0 +1 @@
+docker compose -f docker-compose.dedicated-server.yml build \ No newline at end of file
diff --git a/docker-compose.dedicated-server.ghcr.yml b/docker-compose.dedicated-server.ghcr.yml
new file mode 100644
index 00000000..8bcaf484
--- /dev/null
+++ b/docker-compose.dedicated-server.ghcr.yml
@@ -0,0 +1,27 @@
+services:
+ minecraft-lce-dedicated-server:
+ image: ghcr.io/kuwacom/minecraft-lce-dedicated-server:nightly
+ container_name: minecraft-lce-dedicated-server
+ restart: unless-stopped
+ tty: true
+ stdin_open: true
+ environment:
+ TZ: ${TZ:-Asia/Tokyo}
+ WINEARCH: win64
+ WINEPREFIX: /var/opt/wineprefix64
+ WINEDEBUG: -all
+ # linux require use file stdin
+ SERVER_CLI_INPUT_MODE: ${SERVER_CLI_INPUT_MODE:-stream}
+ # minimum required virtual screen
+ XVFB_DISPLAY: ${XVFB_DISPLAY:-:99}
+ XVFB_SCREEN: ${XVFB_SCREEN:-720x1280x16}
+ volumes:
+ # - wineprefix64:/var/opt/wineprefix64
+ - ./server-data:/srv/persist
+ ports:
+ - "25565:25565/tcp"
+ - "25565:25565/udp"
+ stop_grace_period: 30s
+
+# volumes:
+# wineprefix64:
diff --git a/docker-compose.dedicated-server.yml b/docker-compose.dedicated-server.yml
new file mode 100644
index 00000000..4a0d3313
--- /dev/null
+++ b/docker-compose.dedicated-server.yml
@@ -0,0 +1,31 @@
+services:
+ minecraft-lce-dedicated-server:
+ build:
+ context: .
+ dockerfile: docker/dedicated-server/Dockerfile
+ args:
+ MC_RUNTIME_DIR: ${MC_RUNTIME_DIR:-x64/Minecraft.Server/Release}
+ container_name: minecraft-lce-dedicated-server
+ restart: unless-stopped
+ tty: true
+ stdin_open: true
+ environment:
+ TZ: ${TZ:-Asia/Tokyo}
+ WINEARCH: win64
+ WINEPREFIX: /var/opt/wineprefix64
+ WINEDEBUG: -all
+ # linux require use file stdin
+ SERVER_CLI_INPUT_MODE: ${SERVER_CLI_INPUT_MODE:-stream}
+ # minimum required virtual screen
+ XVFB_DISPLAY: ${XVFB_DISPLAY:-:99}
+ XVFB_SCREEN: ${XVFB_SCREEN:-64x64x16}
+ volumes:
+ # - wineprefix64:/var/opt/wineprefix64
+ - ./server-data:/srv/persist
+ ports:
+ - "25565:25565/tcp"
+ - "25565:25565/udp"
+ stop_grace_period: 30s
+
+# volumes:
+# wineprefix64:
diff --git a/docker/dedicated-server/Dockerfile b/docker/dedicated-server/Dockerfile
new file mode 100644
index 00000000..14760afd
--- /dev/null
+++ b/docker/dedicated-server/Dockerfile
@@ -0,0 +1,33 @@
+FROM debian:bookworm-slim
+
+ARG DEBIAN_FRONTEND=noninteractive
+# basically, it only works with a Release build.(libs are not included in Debug build)
+ARG MC_RUNTIME_DIR=x64/Minecraft.Server/Release
+
+RUN dpkg --add-architecture i386 \
+ && apt-get update \
+ && apt-get install -y --no-install-recommends \
+ ca-certificates \
+ wine \
+ wine64 \
+ wine32:i386 \
+ xvfb \
+ tini \
+ && rm -rf /var/lib/apt/lists/*
+
+ENV WINEARCH=win64
+ENV WINEPREFIX=/var/opt/wineprefix64
+
+WORKDIR /srv/mc
+
+COPY ${MC_RUNTIME_DIR}/Minecraft.Server.exe /srv/mc/Minecraft.Server.exe
+COPY ${MC_RUNTIME_DIR}/iggy_w64.dll /srv/mc/iggy_w64.dll
+COPY ${MC_RUNTIME_DIR}/Common /srv/mc/Common
+COPY ${MC_RUNTIME_DIR}/Windows64 /srv/mc/Windows64
+COPY docker/dedicated-server/entrypoint.sh /usr/local/bin/entrypoint.sh
+
+RUN chmod 0755 /usr/local/bin/entrypoint.sh \
+ && mkdir -p /var/opt/wineprefix64 /srv/mc \
+ && test -f /srv/mc/Minecraft.Server.exe
+
+ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"]
diff --git a/docker/dedicated-server/entrypoint.sh b/docker/dedicated-server/entrypoint.sh
new file mode 100644
index 00000000..a8f8907a
--- /dev/null
+++ b/docker/dedicated-server/entrypoint.sh
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SERVER_DIR="/srv/mc"
+SERVER_EXE="Minecraft.Server.exe"
+# ip & port are fixed since they run inside the container
+SERVER_PORT="25565"
+SERVER_BIND_IP="0.0.0.0"
+
+PERSIST_DIR="/srv/persist"
+WINE_CMD=""
+
+ensure_persist_file() {
+ local persist_path="$1"
+ local runtime_path="$2"
+ local default_text="$3"
+
+ if [ ! -f "${persist_path}" ]; then
+ if [ -f "${runtime_path}" ] && [ ! -L "${runtime_path}" ]; then
+ cp -f "${runtime_path}" "${persist_path}"
+ else
+ printf '%b' "${default_text}" > "${persist_path}"
+ fi
+ fi
+
+ if [ -e "${runtime_path}" ] && [ ! -L "${runtime_path}" ]; then
+ rm -f "${runtime_path}"
+ fi
+
+ ln -sfn "${persist_path}" "${runtime_path}"
+}
+
+if [ ! -d "$SERVER_DIR" ]; then
+ echo "[error] Server directory not found: $SERVER_DIR" >&2
+ exit 1
+fi
+
+cd "$SERVER_DIR"
+
+if [ ! -f "$SERVER_EXE" ]; then
+ echo "[error] ${SERVER_EXE} not found in ${SERVER_DIR}" >&2
+ echo "[hint] Rebuild image with a valid MC_RUNTIME_DIR build arg that contains dedicated server runtime files." >&2
+ exit 1
+fi
+
+mkdir -p "${PERSIST_DIR}"
+
+# created because it is not implemented on the server side
+mkdir -p "${PERSIST_DIR}/GameHDD"
+
+ensure_persist_file "${PERSIST_DIR}/server.properties" "server.properties" ""
+ensure_persist_file "${PERSIST_DIR}/banned-players.json" "banned-players.json" "[]\n"
+ensure_persist_file "${PERSIST_DIR}/banned-ips.json" "banned-ips.json" "[]\n"
+
+# differs from the structure, but it’s reorganized into a more manageable structure to the host side
+if [ -e "Windows64/GameHDD" ] && [ ! -L "Windows64/GameHDD" ]; then
+ rm -rf "Windows64/GameHDD"
+fi
+ln -sfn "${PERSIST_DIR}/GameHDD" "Windows64/GameHDD"
+
+# for compatibility with other images
+if command -v wine64 >/dev/null 2>&1; then
+ WINE_CMD="wine64"
+elif [ -x "/usr/lib/wine/wine64" ]; then
+ WINE_CMD="/usr/lib/wine/wine64"
+elif command -v wine >/dev/null 2>&1; then
+ WINE_CMD="wine"
+else
+ echo "[error] No Wine executable found (wine64/wine)." >&2
+ exit 1
+fi
+
+if [ ! -d "${WINEPREFIX}" ] || [ -z "$(ls -A "${WINEPREFIX}" 2>/dev/null)" ]; then
+ mkdir -p "${WINEPREFIX}"
+fi
+
+# in the current implementation, a virtual screen is required because the client-side logic is being called for compatibility
+if [ -z "${DISPLAY:-}" ]; then
+ export DISPLAY="${XVFB_DISPLAY:-:99}"
+ XVFB_SCREEN="${XVFB_SCREEN:-64x64x16}"
+ Xvfb "${DISPLAY}" -nolisten tcp -screen 0 "${XVFB_SCREEN}" >/tmp/xvfb.log 2>&1 &
+ sleep 0.2
+fi
+
+args=(
+ -port "${SERVER_PORT}"
+ -bind "${SERVER_BIND_IP}"
+)
+
+echo "[info] Starting ${SERVER_EXE} on ${SERVER_BIND_IP}:${SERVER_PORT}"
+exec "${WINE_CMD}" "${SERVER_EXE}" "${args[@]}"
diff --git a/start-dedicated-server.sh b/start-dedicated-server.sh
new file mode 100644
index 00000000..8169601c
--- /dev/null
+++ b/start-dedicated-server.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
+COMPOSE_FILE="${SCRIPT_DIR}/docker-compose.dedicated-server.ghcr.yml"
+SERVICE_NAME="minecraft-lce-dedicated-server"
+PERSIST_DIR="${SCRIPT_DIR}/server-data"
+
+if [[ ! -f "${COMPOSE_FILE}" ]]; then
+ echo "[error] docker-compose file not found: ${COMPOSE_FILE}" >&2
+ exit 1
+fi
+
+if command -v docker >/dev/null 2>&1; then
+ COMPOSE_CMD=(docker compose)
+elif command -v docker-compose >/dev/null 2>&1; then
+ COMPOSE_CMD=(docker-compose)
+else
+ echo "[error] docker compose is not available." >&2
+ exit 1
+fi
+
+if [[ "${#}" -gt 1 ]]; then
+ echo "Usage: $0 [--no-pull]" >&2
+ exit 1
+fi
+
+DO_PULL=1
+if [[ "${#}" -eq 1 ]]; then
+ if [[ "${1}" == "--no-pull" ]]; then
+ DO_PULL=0
+ else
+ echo "Usage: $0 [--no-pull]" >&2
+ exit 1
+ fi
+fi
+
+if [[ "${DO_PULL}" -eq 1 ]]; then
+ echo "[info] Pulling latest image from GHCR..."
+ "${COMPOSE_CMD[@]}" -f "${COMPOSE_FILE}" pull "${SERVICE_NAME}"
+fi
+
+echo "[info] Starting dedicated server..."
+"${COMPOSE_CMD[@]}" -f "${COMPOSE_FILE}" up -d "${SERVICE_NAME}"
+echo "[info] Dedicated server started."